Почему float не может правильно отображать pow (2., 60.)? - PullRequest
0 голосов
/ 28 декабря 2018

Из Компьютерное представление чисел с плавающей запятой Я изучил представление компьютера с плавающей запятой.
В соответствии с руководством, для 32-разрядного числа с плавающей запятой, наименьшее положительное нормализованное число, которое может быть сохранено,2 ^ (- 126) ,, а наибольшее нормированное число - (2-2 ^ (- 23)) * 2 ^ (127) ≈ 2 ^ (128).Однако точность ограничена 23-битным значением.

По моему мнению, 32-битный float может представлять 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 голосов
/ 29 декабря 2018

Это не ошибка 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

Поведение Microsoft разрешено стандартом C, но математически, конечно, не очень хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...