当前位置:网站首页>std::uniform_real_distribution的一个bug引发的服务器崩溃
std::uniform_real_distribution的一个bug引发的服务器崩溃
2022-08-09 15:08:00 【AlbertS】
前言
近日发生一次线上游戏服务器宕机问题,通过日志和core文件信息定位到崩溃的函数,但是崩溃的位置却是一段很长时间都没有改动过的代码,起初怀疑是配置数据的问题,但仔细查看之后均正常,然后又怀疑是玩家旧数据异常导致,但是分析代码逻辑后也没发现问题,最后是一个同事发现生成随机数的代码有bug,导致数组越界了,还真是个意想不到的原因。
崩溃问题
崩溃出现在从数组中随机一个数的逻辑中,其中用到了 std::uniform_real_distribution
这个模板类,示例代码如下:
vector<int> v{
1, 3, 5, 6};
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<float> dis(0, 1.0f);
int n = static_cast<int>(dis(gen) * v.size());
return v[n];
之前也了解过 std::uniform_real_distribution<float> dis(0, 1.0f);
这个用法,他应该返回的范围是 [0, 1.0)
内左闭右开的浮点数,所以最终计算出的 n
的值应该为 [0, n-1]
范围内的整数,所以这段代码不应该有问题,但是问题却恰恰出现在 std::uniform_real_distribution
的身上。
std::uniform_real_distribution<> 的bug
std::uniform_real_distribution 这个模板类定义在头文件 <random>
中,是C++11新加的内容,定义如下:
template< class RealType = double >
class uniform_real_distribution;
可产生均匀分布在区间 [a, b) 上的随机浮点值 x。
但是这个函数有个bug,它有时候会返回边界值b,也就是说实际范围变成了 [a, b]
。 可以通过 cppreference.com - uniform_real_distribution查到,具体描如下:
It is difficult to create a distribution over the closed interval [a,b] from this distribution. Using std::nextafter(b, std::numeric_limits::max()) as the second parameter does not always work due to rounding error.
Most existing implementations have a bug where they may occasionally return b (GCC #63176 LLVM #18767 MSVC STL #1074). This was originally only thought to happen when RealType is float and when LWG issue 2524 is present, but it has since been shown that neither is required to trigger the bug.
关于这个bug还可以看一下这个帖子的讨论:
看得时候注意一下这段描述
This problem can also occur with std::uniform_real_distribution; the solution is the same, to specialize the distribution on double and round the result towards negative infinity in float.
bug 重现方法
这个bug有多种变体,其中一个就是说它和 generate_canonical
产生随机数有关
#include <iostream>
#include <limits>
#include <random>
int main()
{
std::mt19937 rng;
std::seed_seq sequence{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
rng.seed(sequence);
rng.discard(12 * 629143 + 6);
float random = std::generate_canonical<float, std::numeric_limits<float>::digits>(rng);
if (random == 1.0f)
{
std::cout << "Bug!\n";
}
return 0;
}
此段代码在编译器 g++ 5.4.0
上编译执行时能重现,但是在 g++ 10.0.3
上已经被修复无法重现了,再看下面这段代码:
#include <iostream>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0, 1.0f);
while (true)
{
float f = dis(gen);
if (f >= 1.0)
{
std::cout << "BUG\n";
break;
}
}
return 0;
}
这段代码无论是 g++ 5.4.0
版本还是 g++ 10.0.3
都能重现打印出 BUG
,这个问题在于模板默认是 double
类型,最后转化成 float
来使用,我按照建议之前帖子中的建议,都改成 double
来使用,之后一直运行了10来天再没出现过随机到边界值的问题。
总结
- 标准库中的内容很权威,但是不保证一定是正确的,可以持有怀疑态度
- std::uniform_real_distribution的历史版本是有bug,几乎各个编译器都出现过随机到边界值的情况
- 这个bug其实在文档中已经指出了,所以大家看文档时还是要仔细一点,往往使用不规范也容易造成bug
适当的放松是生活的调味剂,有时候真的需要肆意挥霍一下,一张一弛,生活之道~
边栏推荐
猜你喜欢
Heap series_0x06: NT global flags and gflags.exe one page
经典题型(一)
2022高教社杯 国赛数学建模 C题思路
第二章:创建交互式地图(2.4-2.6)
C语言基本数据类型的存储大小、取值范围、输出格式的解剖
为什么四个字节的float表示的范围比八个字节的long要广
字典树、并查集相关:实现Trie、搜索推荐系统、朋友圈、被围绕的区域(未做) ...
Access Characteristics of Constructor under Inheritance Relationship
The web project accesses static resources inside the jar
【挨踢(IT)初体验】
随机推荐
2022高教社杯 国赛数学建模 C题思路
二叉树详解
学习编程的第九天
字符菱形的代码
Foreword: About the author Dr. Wu Qiusheng and an introduction to the book
【科普】关于平板电脑的那些事
C语言三子棋详解
第四章:使用本地地理空间数据(4.6-4.14)
5. Visualizing Geospatial Data
Chapter 4: Using Local Geospatial Data (4.1-4.5)
uniapp封装全局js并在页面引用
Super hot summer air conditioner
动态规划套题:零钱兑换、完全平方数
2022华数杯建模B题思路解析
学习编程的第二天
(十)打包和项目部署
三.两数交换 空指针 && 野指针 解引用问题
3. Using Earth Engine Data
C语言分支语句if,switch语句详细讲解
测试工作管理与规范