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

Why is there inaccuracy in the following intermediate type conversion?

以下示例来自 发现现代 C++ 一书的第 14 页,Peter Gottschling。作者声明:

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

long l = 1234567890123;
long l2 = l + 1.0f - 1.0; // imprecise
long l3 = l + (1.0f - 1.0); // precise

这在作者的平台上导致:

l2 = 1234567954431;
l3 = 1234567890123;

我的问题是,究竟是什么导致了这种不精确?是不是因为加减法的左结合性,所以l2被计算为(l + 1.0f) - 1.0?如果是这样,float (see) 的值范围 3.4E +/- 38 (7 digits) 肯定涵盖了值 1234567890123,因此据我所知,缩小范围应该不是问题。

A float 通常是 32 位。与相同大小的 int 相比,您认为它如何实现更大的射程(最大值 ~3.4e38),后者的最大值为 ~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);
}

它以尽可能慢的速度遍历 float 值,即以尽可能小的数量增加值。

0.001000000047497451305389404296875 0.00100000016391277313232421875 0.001000000280328094959259033203125
1 1.00000011920928955078125 1.0000002384185791015625
100000000 100000008 100000016

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

第一行

l2 = l + 1.0f

导致生成一个中间浮点数,并且 1234567890123 将不适合浮点数而不损失精度。然后减去 1。你的答案是正确的

第2行,不要写这个。在我看来,这是未定义的行为。你有一个来自括号中的浮点数,被添加到一个 int,它创建一个浮点数,然后在评估结束时被转换回一个 int。除非我读错了标准,否则它也应该给出不精确的值,尽管编译器优化可能会阻止它。

除非你想把自己弄糊涂,否则在添加时显式转换,如果这样的话