向无符号的64位值添加double值会产生奇怪的结果

时间:2012-03-07 03:43:37

标签: c casting floating-point

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;

但是这似乎也没有帮助。关于可能出现什么问题的任何想法?

2 个答案:

答案 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-------------->

如果要避免这种舍入,则应该通过将操作数转换为整数来完全避免浮点运算。 (或者仅使用119238

答案 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; 

那会给你你想要的答案。它最初将双精度转换为整数类型,这是精确添加的。