int main(int argc, char *argv[])
{
uint64_t length = 0x4f56aa5d4b2d8a80;
uint64_t new_length = 0;
new_length = length + 119.000000;
printf("new length 0x%"PRIx64"\n",new_length);
new_length = length + 238.000000;
printf("new length 0x%"PRIx64"\n",new_length);
return 0;
}
使用上面的代码。我将两个不同的double值添加到无符号的64位整数。我在两种情况下都得到完全相同的结果。程序的输出如下所示
$./a.out
new length 0x4f56aa5d4b2d8c00
new length 0x4f56aa5d4b2d8c00
我希望得到两个不同的结果,但事实并非如此。我也尝试将uint64_t
值类型转换为double
,如
new_length = (double)length + 119.000000;
但是这似乎也没有帮助。关于可能出现什么问题的任何想法?
答案 0 :(得分:7)
由于您添加了浮点操作数,因此两个操作数都隐式转换为double
,并且使用浮点运算完成添加。
但是,double
没有足够的精度来准确保存以下任一值:
0x4f56aa5d4b2d8a80 + 119.0 (requires 63 bits of precision)
0100111101010110101010100101110101001011001011011000101011110111
<-------------------63 bits of precision---------------------->
0x4f56aa5d4b2d8a80 + 238.0 (requires 62 bits of precision)
0100111101010110101010100101110101001011001011011000101101101110
<-------------------62 bits of precision--------------------->
标准IEEE双精度仅具有 53位精度 。
结果是它们都被四舍五入到相同的最终值:
0x4f56aa5d4b2d8c00 (53 bits of precision)
0100111101010110101010100101110101001011001011011000110000000000
<-----------------53 bits of precision-------------->
如果要避免这种舍入,则应该通过将操作数转换为整数来完全避免浮点运算。 (或者仅使用119
和238
)
答案 1 :(得分:3)
浮点运算不精确。随着数字越来越大,低位数的准确性会降低。
0x4f56aa5d4b2d8a80是一个非常大的数字。
中发生了什么
new_length = length + 119.000000;
是否length + 119.000000
被强制转换为双倍,以进行添加。这个双倍是圆润的,相当戏剧性的,因为它太大了。然后在将其分配给new_length
时再次将其转换为整数类型uint64_t。
致电时
new_length = length + 238.000000;
圆形结果的结果恰好相同。
你真正想做的是
new_length = length + (uint64_t)238.0;
那会给你你想要的答案。它最初将双精度转换为整数类型,这是精确添加的。