【问题标题】:adding a double value to an unsigned 64-bit value yields weird results将双精度值添加到无符号 64 位值会产生奇怪的结果
【发布时间】:2012-03-07 03:43:36
【问题描述】:
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;
}

使用上面的代码。我将两个不同的双精度值添加到一个无符号的 64 位整数。在这两种情况下我得到完全相同的结果。程序的输出如下所示

$./a.out
new length  0x4f56aa5d4b2d8c00
new length  0x4f56aa5d4b2d8c00

我希望得到两个不同的结果,但事实并非如此。我还尝试将 uint64_t 值类型转换为 double,如下所示

new_length = (double)length + 119.000000;

但这似乎也没有帮助。你知道可能是什么问题吗?

【问题讨论】:

标签: c casting floating-point


【解决方案1】:

由于您添加了一个浮点操作数,因此两个操作数都被隐式转换为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 代替)

【讨论】:

    【解决方案2】:

    浮点运算不精确。随着数字变大,低位数字的准确性会降低。

    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; 
    

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

    【讨论】:

    • 浮点精确的。您可以一遍又一遍地重复相同的实验并获得相同的结果。根据您对真实数字的经验,这些结果可能与您的预期不一致,但它们肯定是准确的。
    猜你喜欢
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    • 2022-01-08
    • 2015-03-25
    相关资源
    最近更新 更多