为什么某些浮点计算会改变它们的方式?(例如 123456789f +1 = 123456792)

ght*_*rpe 1 floating-point precision floating-accuracy

我试图更好地理解浮点运算、发生和产生的错误,以及为什么结果会这样。以下是我目前正在研究的 3 个示例:

1.) 0.1+0.1 +0.1 +0.1 +0.1 +0.1 +0.1 +0.1 +0.1 +0.1 -1.0 = -1.1102230246251565E-16又名添加0.1 10次给我一个略小于1.0的数字。但是,0.1表示(作为双精度)略大于0.1。另外 *0.1*3* 略大于0.3,但 *0.1*8* 略小于0.8

2.) 123456789f+1 = 123456792 和 123456789f +4 = 123456800。

这些结果是怎么回事?这一切对我来说仍然有点神秘。

Eri*_*hil 6

典型的现代处理器和编程语言使用 IEEE-754 算术(或多或少),其中 32 位二进制浮点float和 64 位二进制浮点double。在 中double,使用 53 位有效数。这意味着,当十进制数字转换为 时double,它会转换为某个数字s \xe2\x80\xa2 f \xe2\x80\xa22 e,其中s是符号(+1 或 \xe2\x88\x921) ,f是可以用 53 位表示的无符号整数,e是 \xe2\x88\x921074 和 971 之间的整数(含)。(或者,如果要转换的数字太大,结果可能是+无穷大或-无穷大。)(那些了解浮点格式的人可能会抱怨指数正确地在 \xe2\x88\x921023 和 1023 之间,但是我已经移动了有效数以使其成为整数。我描述的是数学值,而不是编码。)

\n

将 .1 转换为double3602879701896397 / 36028797018963968,因为在所需形式的所有数字中,该数字最接近 0.1。分母是 2 \xe2\x88\x9255,因此e是 \xe2\x88\x9255 。

\n

当我们添加其中两个时,我们得到 7205759403792794 / 36028797018963968 。没关系,分子仍然小于 2 53,因此它符合格式。

\n

当我们加上第三个3602879701896397 / 36028797018963968时,数学结果是10808639105689191 / 36028797018963968。不幸的是,分子太大了;它大于 2 53 (9007199254740992)。因此浮点硬件无法返回该数字。它必须以某种方式使其适合。

\n

如果我们将分子和分母除以二,我们得到 5404319552844595.5 / 18014398509481984。这具有相同的值,但分子不是整数。为了使其适合,硬件将其四舍五入为整数。当分数恰好为 1/2 时,规则是四舍五入以使结果均匀,因此硬件返回 5404319552844596 / 18014398509481984。

\n

接下来,我们取出当前总和 5404319552844596 / 18014398509481984,并再次添加 3602879701896397 / 36028797018963968。这次,总和为 7205759403792794.5 / 18014398509481984。在本例中,硬件向下舍入,返回 7205759403792794 / 18014398509481984。

\n

然后我们将 7205759403792794 / 18014398509481984 和 3602879701896397 / 36028797018963968 相加,总和为 9007199254740992.5 / 18014398509481984。分子不仅有分数而且大于 2 53。所以我们必须再次减少它,这会产生 4503599627370496.25 / 9007199254740992。将分子四舍五入到整数会产生 4503599627370496 / 9007199254740992。

\n

这正好是 1/2。此时,舍入误差正好抵消了;0.1 加五次正好得到 0.5。

\n

当我们将 4503599627370496 / 9007199254740992 和 3602879701896397 / 36028797018963968 相加时,结果恰好是 5404319552844595.25 / 9007199254740992。硬件向下舍入并返回 5404319552844595 / 9007199254740992。

\n

现在您可以看到我们将重复向下舍入。要将 3602879701896397 / 36028797018963968 添加到累加和中,硬件必须将其分子除以四以使其匹配。这意味着小数部分始终为 0.25,并且会向下舍入。因此接下来的四项总和也向下舍入。我们最终得到 9007199254740991 / 9007199254740992,它小于 1。

\n

使用float代替 时double,分子必须适合 24 位,因此它必须小于 2 24 (16777216)。因此,即使在进行任何算术运算之前,123456789 也太大了。它必须表示为 15432099 \xe2\x80\xa2 2 3,即 123456792。加 1 的精确数学结果是 15432099.125 \xe2\x80\xa2 2 3,将该尾数舍入为整数得到 15432099 \xe2\ x80\xa2 2 3,所以没有变化。但是,如果添加四个,结果为 15432099.5 \xe2\x80\xa2 2 3,四舍五入为 15432100 \xe2\x80\xa2 2 3

\n