【发布时间】:2014-10-29 09:17:48
【问题描述】:
我一直认为随机数会介于零和一之间,没有1,即它们是来自半开区间 [0,1) 的数字。 std::generate_canonical 的 documention on cppreference.com 证实了这一点。
但是,当我运行以下程序时:
#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;
}
它给了我以下输出:
Bug!
即它为我生成了一个完美的1,这会导致我的 MC 集成出现问题。这是有效的行为还是我这边有错误?这给出了与 G++ 4.7.3 相同的输出
g++ -std=c++11 test.c && ./a.out
和clang 3.3
clang++ -stdlib=libc++ -std=c++11 test.c && ./a.out
如果这是正确的行为,我该如何避免1?
编辑 1:来自 git 的 G++ 似乎也遇到了同样的问题。我来了
commit baf369d7a57fb4d0d5897b02549c3517bb8800fd
Date: Mon Sep 1 08:26:51 2014 +0000
使用~/temp/prefix/bin/c++ -std=c++11 -Wl,-rpath,/home/cschwan/temp/prefix/lib64 test.c && ./a.out 编译得到相同的输出,ldd 产生
linux-vdso.so.1 (0x00007fff39d0d000)
libstdc++.so.6 => /home/cschwan/temp/prefix/lib64/libstdc++.so.6 (0x00007f123d785000)
libm.so.6 => /lib64/libm.so.6 (0x000000317ea00000)
libgcc_s.so.1 => /home/cschwan/temp/prefix/lib64/libgcc_s.so.1 (0x00007f123d54e000)
libc.so.6 => /lib64/libc.so.6 (0x000000317e600000)
/lib64/ld-linux-x86-64.so.2 (0x000000317e200000)
编辑 2:我在这里报告了该行为:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63176
编辑 3:clang 团队似乎意识到了这个问题:http://llvm.org/bugs/show_bug.cgi?id=18767
【问题讨论】:
-
@David Lively
1.f == 1.f在所有情况下(所有情况下都有什么?我什至没有在1.f == 1.f中看到任何变量;这里只有一个情况:1.f == 1.f,那就是总是true)。请不要进一步传播这个神话。浮点比较总是精确的。 -
@DavidLively:不,不是。比较总是准确的。您的操作数可能不准确如果它们是计算出来的而不是文字。
-
@Galik 任何低于 1.0 的正数都是有效结果。 1.0 不是。就这么简单。舍入无关:代码得到一个随机数并且不对它执行任何舍入。
-
@DavidLively 他说只有一个值比较等于 1.0。该值为 1.0。接近 1.0 的值不等于 1.0。生成函数的作用无关紧要:如果它返回 1.0,它将比较等于 1.0。如果它不返回 1.0,它不会比较等于 1.0。您使用
abs(random - 1.f) < numeric_limits<float>::epsilon的示例检查结果是否接近 1.0,这在这种情况下是完全错误的:这里有接近 1.0 的数字是有效结果,即所有小于1.0. -
@Galik 是的,实现起来会有问题。但是这个麻烦是由实施者来处理的。用户决不能看到 1.0,而且用户必须始终看到所有结果的均等分布。