C 截断时的浮点舍入

C 截断时的浮点舍入,c,floating-point,x86,fpu,C,Floating Point,X86,Fpu,对于x86 FPU专家来说,这可能是一个问题: 我正在尝试编写一个函数,生成[min,max]范围内的随机浮点值。问题是,我的生成器算法(如果你好奇的话,可以使用浮点Mersenne Twister)只返回[1,2]范围内的值——也就是说,我想要一个包含的上界,但我的“源”生成的值来自一个排他上界。这里的问题是,底层生成器返回一个8字节的双精度浮点值,但我只需要一个4字节的浮点值,并且我使用默认的FPU舍入模式Nearest 我想知道的是,在这种情况下,当FPU内部80位值足够接近时,截断本身是

对于x86 FPU专家来说,这可能是一个问题:

我正在尝试编写一个函数,生成[min,max]范围内的随机浮点值。问题是,我的生成器算法(如果你好奇的话,可以使用浮点Mersenne Twister)只返回[1,2]范围内的值——也就是说,我想要一个包含的上界,但我的“源”生成的值来自一个排他上界。这里的问题是,底层生成器返回一个8字节的双精度浮点值,但我只需要一个4字节的浮点值,并且我使用默认的FPU舍入模式Nearest

我想知道的是,在这种情况下,当FPU内部80位值足够接近时,截断本身是否会导致返回值包含max,或者我是否应该在max值乘以中间随机数之前增加max值的有效位[1,2],或者我是否应该改变FPU模式。当然,还有其他想法

以下是我当前使用的代码,我确实验证了1.0f解析为0x3f800000:

float MersenneFloat( float min, float max )
{
    //genrand returns a double in [1,2)
    const float random = (float)genrand_close1_open2(); 
    //return in desired range
    return min + ( random - 1.0f ) * (max - min);
}
如果有区别的话,这需要在Win32 MSVC++和Linux gcc上工作。另外,使用任何版本的SSE优化是否会改变答案


编辑:答案是肯定的,在这种情况下,从double到float的截断足以导致结果包含max。有关更多信息,请参阅Crashworks的答案。

如果您调整舍入,使其包含范围的两端,那么这些极值的可能性是否仅为任何非极值的一半

使用截断,永远不会包含最大值

你确定你真的需要最大值吗?你几乎没有机会完全达到最大值

这就是说,您可以利用放弃精度这一事实,执行以下操作:

float MersenneFloat( float min, float max )
{
    double random = 100000.0; // just a dummy value
    while ((float)random > 65535.0)
    {
        //genrand returns a double in [1,2)
        double random = genrand_close1_open2() - 1.0; // now it's [0,1)
        random *= 65536.0; // now it's [0,65536). We try again if it's > 65535.0
    }
    //return in desired range
    return min + float(random/65535.0) * (max - min);
}
请注意,现在,每次调用MersenneFloat时,都有可能多次调用genrand。因此,在一个封闭的时间间隔内,您已经放弃了可能的性能。由于您正在从double向下转换为float,因此最终将不会牺牲精度


编辑:改进的算法

SSE ops将微妙地改变此算法的行为,因为它们没有中间的80位表示——数学实际上是在32或64位中完成的。好消息是,您可以轻松地测试它,并通过简单地将/ARCH:SSE2命令行选项指定给MSVC,w,来查看它是否会改变您的结果这将导致它使用SSE标量运算,而不是x87 FPU指令进行普通浮点运算

我不确定整数边界周围的精确舍入行为是什么,但您可以测试一下,当1.999..通过eg从64位舍入到32位时会发生什么


编辑,结果:original poster运行了此测试,发现使用截断后,1.99999在有/无/arch:SSE2的情况下将取整为2。

在我看来,如果我只使用截断,答案是肯定的,但如果我增加最大有效位,答案将是否定的。是的,我需要最大值包含在内(这是一份图书馆职能合同)。与在乘法之前增加我的最大值的有效位相比,按你的方式进行有什么好处吗?这可能也行。但是,在某个地方,你可能需要进行拒绝测试,或者值的分布不完美。这个问题的一个类似问题是,例如,生成一个0-256 fr的整数随机整数0-65535。它只是不均匀映射。事实上,我刚刚尝试了Crashworks测试建议,而截断实际上是四舍五入的。现在为什么我没有想到在我运行的其他测试中运行该测试:)我确实发现,通过截断,1.99999在有/无/arch:SSE2的情况下将取整为2。谢谢!很高兴能提供帮助--我很好奇测试的结果会是什么。
static uint64 OnePointNineRepeating = 0x3FF FFFFF FFFF FFFF // exponent 0 (biased to 1023), all 1 bits in mantissa
double asDouble = *(double *)(&OnePointNineRepeating);
float asFloat = asDouble;
return asFloat;