首页 > 解决方案 > 为什么下面的中间类型转换会出现不准确?

问题描述

以下示例来自《发现现代 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?如果是这样,(请参阅)的值范围肯定3.4E +/- 38 (7 digits)涵盖floatvalue1234567890123因此据我所知,缩小应该不是问题。

标签: c++ieee-754

解决方案


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);
}

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

0.001000000047497451305389404296875 0.00100000016391277313232421875 0.001000000280328094959259033203125
1 1.00000011920928955078125 1.0000002384185791015625
100000000 100000008 100000016

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


推荐阅读