Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 1.0是std::generate_canonical的有效输出吗?_C++_C++11_Random - Fatal编程技术网

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

C++ 1.0是std::generate_canonical的有效输出吗?,c++,c++11,random,C++,C++11,Random,我一直认为随机数应该介于0和1之间,没有1,也就是说,它们是来自半开区间[0,1]的数字。std::generate\u canonical的函数证实了这一点 但是,当我运行以下程序时: #include <iostream> #include <limits> #include <random> int main() { std::mt19937 rng; std::seed_seq sequence{0, 1, 2, 3, 4, 5,

我一直认为随机数应该介于0和1之间,没有
1
,也就是说,它们是来自半开区间[0,1]的数字。
std::generate\u 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;
}
i、 它生成了一个完美的
1
,这会导致我的MC集成出现问题。这是有效的行为还是我这边有错误?这与G++4.7.3的输出相同

g++ -std=c++11 test.c && ./a.out
和叮当声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:我在这里报告了该行为:


编辑3:clang团队似乎意识到了这个问题:

根据标准,
1.0
无效

C++11§26.5.7.2函数模板生成规范 根据第26.5.7.2节所述模板实例化的每个函数将所提供的统一随机数生成器
g
的一个或多个调用的结果映射到指定实类型的一个成员,这样,如果
g
生成的值gi均匀分布,则实例化的结果tj,0≤ tj<1的分布应尽可能均匀,如下所述


问题在于从
std::mt19937
std::uint\u fast32\u t
)的编码域映射到
浮点
;标准描述的算法给出了不正确的结果(与其对算法输出的描述不一致)如果当前IEEE754舍入模式不是舍入到负无穷大(请注意,默认为舍入到最近值),则会发生精度损失

带种子的mt19937的7549723次输出为4294967257(
0xFFFFD9U
),当四舍五入为32位浮点时,该值为
0x1p+32
,当四舍五入为32位浮点时,该值等于mt19937、4294967295(
0xFFFFFFU
)的最大值

如果标准规定从URNG的输出转换为
generate\u canonical
RealType
时,舍入将朝负无穷大方向执行,则可以确保正确的行为;在这种情况下,这将给出正确的结果。作为QOI,最好由libstdc++进行此更改

通过此更改,
1.0
将不再生成;而是
00x1.fffff ep-N
){ rf=std::nextafter(rf,-std::numeric_limits::infinity()); }

这个问题也可能发生在
std::uniform\u real\u distribution
中;解决方案是一样的,专门化
double
上的分布,并在
float
中将结果取整为负无穷大我刚刚遇到了一个与
uniform\u real\u distribution
类似的问题,下面是我对s的解释标准在这个问题上的吝啬措辞:

该标准总是用数学来定义数学函数,而不是用IEEE浮点(因为该标准仍然假装浮点可能不是IEEE浮点)。因此,每当你在标准中看到数学术语时,它都是在谈论真正的数学,而不是IEEE

该标准规定
均匀实分布(0,1)(g)
生成规范分布(g)
都应返回半开范围[0,1]内的值。但这些都是数学值。当您在半开范围内取实数时[0,1),并将其表示为IEEE浮点,那么,它将四舍五入到
T(1.0)
的时间占很大一部分

T
float
(24个尾数位)时,我们期望看到
均匀实分布(0,1)(g)==1.0f
大约2^25次中的1

T
double
(53个尾数位)时,我们期望看到
均匀实分布(0,1)(g)=1.0
大约2^54次。我没有耐心测试这个期望

我的理解是,这种行为很好。它可能会冒犯我们的“半开放范围”意识,即声称返回数字“小于1.0”的发行版实际上可以返回等于
1.0
;但这是“1.0”的两种不同含义,参见?第一个是数学1.0;第二个是IEEE单精度浮点数
1.0
。几十年来,我们一直被教导不要比较浮点数以获得精确的相等性


无论你将随机数输入什么算法,都不会在意它有时是否精确地得到
1.0
。除了数学运算之外,你对浮点数无能为力,一旦你做了一些数学运算,你的代码就必须处理舍入。即使你可以合理地假设
generate_canonical(g)!=1.0f
,由于舍入,你仍然无法假设
generate_canonical(g)+1.0f!=2.0f
——你无法摆脱它;那么为什么我们在这个例子中假装你可以呢?

@David Lively
1.f==1.f
(所有的情况都有哪些?我甚至没有在
1.f==1.f
中看到任何变量;这里只有一种情况:
1.f==1.f
,这总是
正确的
)。请不要进一步散布这个神话。浮点比较总是精确的。@DavidLive:不,不是。比较总是精确的。如果操作数是计算的,而不是文字,则可能不精确。@Galik任何低于1.0的正数都是有效的结果。1.0不是。就这么简单。舍入
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)
    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());
    }
template<class F>
void test(long long N, const F& get_a_float) {
    int count = 0;
    for (long long i = 0; i < N; ++i) {
        float f = get_a_float();
        if (f == 1.0f) {
            ++count;
        }
    }
    printf("Expected %d '1.0' results; got %d in practice\n", (int)(N >> 25), count);
}

int main() {
    std::mt19937 g(std::random_device{}());
    auto N = (1uLL << 29);
    test(N, [&g]() { return std::uniform_real_distribution<float>(0,1)(g); });
    test(N, [&g]() { return std::generate_canonical<float, 32>(g); });
}
Expected 16 '1.0' results; got 19 in practice
Expected 16 '1.0' results; got 11 in practice