对于x86 FPU专家来说,这可能是一个问题:
我正在尝试编写一个函数,该函数生成范围为[min,max]的随机浮点值。问题是我的生成器算法(如果您想知道,浮点数Mersenne Twister)仅返回[1,2]范围内的值-即,我想要一个包含范围的上限,但是我的“源”生成的值是从专属上限。这里的问题是底层生成器返回一个8字节的double,但是我只想要4字节的float,并且我使用的是默认的FPU舍入模式Nearest。
我想知道的是,在这种情况下,截断本身是否会导致FPU内部80位值足够接近时我的返回值包含max,或者我是否应该在将max乘以x之前增加其有效位数? [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的答案。
最佳答案
SSE操作将巧妙地改变此算法的行为,因为它们没有中间的80位表示形式-数学实际上是用32位或64位完成的。好消息是,您可以通过简单地将/ ARCH:SSE2命令行选项指定为MSVC来轻松测试它并查看它是否改变了结果,这将导致它使用SSE标量ops代替x87 FPU指令进行普通浮点运算数学。
我不确定确切的四舍五入行为是围绕整数边界的,但是您可以测试一下,例如,将1.999 ..通过64位从32位舍入为32位时会发生什么。
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;
编辑,结果:原始发布者进行了此测试,发现带有截断的1.99999在带/ arch:SSE2和不带/ arch:SSE2的情况下均向上舍入为2。