为什么浮动不能正确表示pow(2。,60。)?

时间:2018-12-28 14:37:11

标签: visual-c++ floating-point

我从Computer Representation of Floating Point Numbers学到了计算机的浮点表示法。
根据教程,对于32位浮点数,最小的正归一化 可存储的数字为2 ^(-126),最大归一化的数字为(2-2 ^(-23))* 2 ^(127)≈2 ^(128)。但是,精度受到23位有效数字的限制。

我认为32位浮点数可以表示2 ^ 60而没有任何错误,因为:

  1. 符号:1
  2. 指数:10111011(十进制187,即60 + 127)
  3. 有效位数:0000 ... 0000(23个零)

使用指数和有效位数的隐藏位(1)表示2 ^ 60就足够了。

我的测试代码如下(VS2013 + win10):

#include <iostream>
#include <math.h>
#include <bitset>

using namespace std;

int main()
{
    union
    {
        float input;   // assumes sizeof(float) == sizeof(int)
        int   output;
    }    data;

    data.input = pow(2., 60.);

    std::bitset<sizeof(float) * CHAR_BIT>   bits(data.output);


    std::cout << "Total: " << bits << std::endl;


    cout << "Sign: " << bits[31] << endl << "Exponent: ";

    for (int i = 30; i > 22; i--)
    {
        cout << bits[i];
    }
    cout << endl << "Significand: ";

    for (int i = 22; i >= 0; i--)
    {
        cout << bits[i];
    }
    cout << endl;


    cout.precision(20);
    cout << data.input << endl;
    printf("%f", data.input);
}

然后我得到输出:

    Total: 01011101100000000000000000000000
    Sign: 0
    Exponent: 10111011
    Significand: 00000000000000000000000
    1152921504606847000
    1152921504606847000.000000

我打印二进制表示形式,这是正确的。但令我感到困惑的是,为什么最后三位数字为零。正确的输出应为1152921504606846976。

此外,我将代码更改如下:

#include <iostream>
#include <math.h>
#include <bitset>

using namespace std;

int main()
{
    for (int i = 1; i < 65; i++)
    {
        union
        {
            float input;   // assumes sizeof(float) == sizeof(int)
            int   output;
        }    data;

        data.input = pow(2, i);

        std::bitset<sizeof(float) * CHAR_BIT>   bits(data.output);


        cout.precision(20);
        cout << i << ": " << data.input << endl;
        //printf("%f\n", data.input);
    }

}

输出为:

1: 2
2 : 4
3 : 8
    ......
55 : 36028797018963968
56 : 72057594037927936
57 : 144115188075855870
58 : 288230376151711740
59 : 576460752303423490
60 : 1152921504606847000
61 : 2305843009213694000
62 : 4611686018427387900
63 : 9223372036854775800
64 : 18446744073709552000

从2 ^ 57开始出现零。谁能告诉我为什么会这样吗?

1 个答案:

答案 0 :(得分:4)

这不是float正确表示2 60 的失败。 Microsoft的软件无法正确地将2 60 转换为十进制(即,失败是在格式代码中,而不是在float算术中,尽管Microsoft的pow之前的实现也不准确)。无论所涉及的实际值是多少,您所使用的软件仅产生17位十进制数字。使用Apple LLVM 10.0.0(clang-1000.11.45.5)编译的同一程序会产生:

55: 36028797018963968
56: 72057594037927936
57: 144115188075855872
58: 288230376151711744
59: 576460752303423488
60: 1152921504606846976
61: 2305843009213693952
62: 4611686018427387904
63: 9223372036854775808
64: 18446744073709551616

C标准允许微软的行为,但是从数学上来说当然不是很好。