为什么下面的中间类型转换会不准确?

Eps*_*way 4 c++ ieee-754

以下示例来自于《发现现代 C++》一书的第 14 页,Peter Gottschling。作者指出:

为了说明这种转换行为,让我们看下面的例子:

long l = 1234567890123;
long l2 = l + 1.0f - 1.0; // imprecise
long l3 = l + (1.0f - 1.0); // precise
Run Code Online (Sandbox Code Playgroud)

这导致作者的平台:

l2 = 1234567954431;
l3 = 1234567890123;
Run Code Online (Sandbox Code Playgroud)

我的问题是究竟是什么导致了这种不精确?是不是由于加法和减法的左结合性,所以l2计算为(l + 1.0f) - 1.0?如果是的话,肯定是值范围3.4E +/- 38 (7 digits)float)覆盖的价值1234567890123,使我的知识狭窄不应该是一个问题。

Hol*_*Cat 5

Afloat通常是 32 位。你如何看待它实现更大的范围(最大值~3.4e38相比同尺寸的)int,为此,最大值为~2.1e9

唯一可能的答案是它无法在达到最大值的过程中存储一些整数。并且可表示数字之间的差距随着绝对值的增加而增加。

考虑这个代码:

#include <cmath>
#include <iostream>
#include <limits>

void foo(float x, int n)
{
    while (n-- > 0)
    {
        std::cout << x << "\n "[n > 0];
        x = std::nextafter(x, std::numeric_limits<float>::infinity());
    }
}

int main()
{
    std::cout.precision(1000);

    foo(0.001, 3);
    foo(1, 3);
    foo(100000000, 3);
}
Run Code Online (Sandbox Code Playgroud)

float以尽可能慢的速度迭代这些值,即以尽可能小的数量递增值。

0.001000000047497451305389404296875 0.00100000016391277313232421875 0.001000000280328094959259033203125
1 1.00000011920928955078125 1.0000002384185791015625
100000000 100000008 100000016
Run Code Online (Sandbox Code Playgroud)

如您所见,在100000000它附近只能表示每 8 个整数。