1.0是std::generate_canonical的有效输出吗?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (57)

我一直以为随机数介于0到1之间,1,即它们是半开区间[0,1]的数。

但是,当我运行以下程序时:

#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)
提问于
用户回答回答于

问题在于从std::mt19937(std::uint_fast32_t)到float如果当前IEEE 754舍入模式不是整数到负-无穷大(请注意默认值为圆整到最近),则标准描述的算法在出现精度损失时会给出不正确的结果。

带着你的种子的mt 19937的7549723产量是4294967257(0xffffffd9u),当舍入到32位浮点数时,0x1p+32,它等于mt 19937,4294967295的最大值(0xffffffffu)当它也被舍入到32位浮点数时。

如果该标准指定在从民革联的输出转换为RealTypegenerate_canonical,四舍五入将对负无穷大执行;在这种情况下,这将给出一个正确的结果。作为QOI,对libstdc++进行此更改是很好的。

有了这个变化,1.0将不再生成;相反,将生成边界值。0x1.fffffep-N0 < N <= 8将更频繁地生成(大约)2^(8 - N - 32)N,视1993年中期的实际分配情况而定。

我建议不要用float带着std::generate_canonical直接;而是在double然后转到负无穷大:

    double rd = std::generate_canonical<double,
        std::numeric_limits<float>::digits>(rng);
    float rf = rd;
    if (rf > rd) {
      rf = std::nextafter(rf, -std::numeric_limits<float>::infinity());
    }

此问题也可能发生在std::uniform_real_distribution<float>;解决方案是相同的,专门化double的负无穷远的结果float

用户回答回答于

根据标准,1.0是无效的。

C++11§26.5.7.2函数模板生成_典范 从本节描述的模板实例化的每个函数都映射提供的统一随机数生成器的一个或多个调用的结果。g的指定RealType的一个成员,这样,如果g都是均匀分布的,实例化的结果是TJ,0≤TJ<1,按以下规定尽可能均匀地分发。

扫码关注云+社区