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

Why do certain floating point calculations turn the way they do? (e.g. 123456789f +1 = 123456792)

我试图更好地理解浮点运算、发生和累积的伴随错误,以及为什么结果会这样。以下是我目前正在处理的 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.

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

典型的现代处理器和编程语言使用 IEEE-754 算法(或多或少),float 为 32 位二进制浮点数,double 为 64 位二进制浮点数。在 double 中,使用了 53 位有效数字。这意味着,当一个十进制数字被转换为double时,它被转换为一些数字sf•2e,其中s为符号(+1或-1),f是一个无符号整数,可以用53位表示,e是-1074和971之间的整数,包括端值。 (或者,如果被转换的数字太大,结果可能是 +infinity 或 -infinity。)(了解浮点格式的人可能会抱怨指数在 −1023 和 1023 之间,但我已经将有效数字使其成为整数。我描述的是数学值,而不是编码。)

将 .1 转换为 double 得到 3602879701896397 / 36028797018963968,因为在所需形式的所有数字中,那个数字最接近 .1。分母为2−55,所以e为−55.

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

当我们加上第三个 3602879701896397 / 36028797018963968 时,数学结果是 10808639105689191 / 36028797018963968。不幸的是,分子太大了;它大于 253 (9007199254740992)。所以浮点硬件不能 return 这个数字。它必须以某种方式使其适合。

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

接下来,我们取当前总和,5404319552844596 / 18014398509481984,并再次添加 3602879701896397 / 36028797018963968。这次,和是 7205759403792794.5 / 18014398509481984。在这种情况下,硬件向下舍入,returning 7205759403792794 / 18014398509481984.

Then we add 7205759403792794 / 18014398509481984 and 3602879701896397 / 36028797018963968, and the sum is 9007199254740992.5 / 18014398509481984. Note that the numerator not only has a fraction but is larger than 253.所以我们必须再次减少它,产生 4503599627370496.25 / 9007199254740992。将分子四舍五入为整数产生 4503599627370496 / 9007199254740992.

正好是 1/2。此时,舍入误差恰好抵消了;将 .1 添加五次恰好产生 .5.

When we add 4503599627370496 / 9007199254740992 and 3602879701896397 / 36028797018963968, the result is exactly 5404319552844595.25 / 9007199254740992. The hardware rounds down and returns 5404319552844595 / 9007199254740992.

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

使用 float 而不是 double,分子必须适合 24 位,因此它必须小于 224 (16777216) .所以 123456789 甚至在任何算术完成之前就太大了。它必须表示为15432099•23,即123456792。加1的精确数学结果为15432099.125•23,并四舍五入整数的有效数为 15432099 • 23,因此没有变化。但是,如果您将四相加,则结果为 15432099.5 • 23,四舍五入为 15432100 • 23.