Почему переменная с плавающей точкой сохраняет значение, сокращая цифры после точки странным образом? - PullRequest
9 голосов
/ 17 февраля 2012

У меня есть эта простая строка кода:

float val = 123456.123456;

когда я печатаю этот val или смотрю в область видимости, он сохраняет значение 123456.13

Хорошо, все в порядке, он не может хранить все эти цифры после точки только в 4 байтах, но почему он делает 13 после точки? Разве это не должно быть 12?

(с использованием vc ++ 2010 express на win32)

Ответы [ 5 ]

10 голосов
/ 17 февраля 2012

В двоичном формате 123456.123456 равно 11110001001000000.000111111001 ... (бесконечно).Округляется до 11110001001000000.001 или 123456.125. Что округляется до 123456,13 при печати. ​​

8 голосов
/ 17 февраля 2012

Значение, сохраненное в val, равно 123456.125.Вы получаете .13, потому что округляете его:

float val = 123456.123456;
printf("%.4f %.2f\n", val, val);

вывод: 123456.1250 123456.13

В этом случае вы должны использовать double, чтобы избежать усечения.Компилятор также должен предупредить вас: "предупреждение C4305: 'инициализация': усечение от 'double' до" float "" .

7 голосов
/ 17 февраля 2012

При представлении в виде числа с плавающей точкой показатель степени вашего числа равен 16 (т. Е. Значение равно его времени богомола 2 ^ 16, или 65536).Тогда мантисса становится

123456.123456 / 65536 = 1.8837909462890625

Для того, чтобы поместиться в 32-битное число с плавающей запятой, мантисса усекается до 23 бит, поэтому теперь она становится 1.883791.При умножении обратно на 65536 оно становится 123456.125.

Обратите внимание на 5 в третьей позиции после десятичной запятой: используемая вами процедура вывода C ++ округляет ее, делая ваше окончательное числовыглядеть как 123456.13.

РЕДАКТИРОВАТЬ Объяснение округления: (комментарий Рика Регана)

округление происходит сначала в двоичном (до 24 бит), в десятичном виде додвоичное преобразование, а затем в десятичное, в printf.Сохраненное значение составляет 1,1110001001000000001 x 2 ^ 16 = 1,8837909698486328125 x 2 ^ 16 = 123456,125.Он печатается как 123456.13, но только потому, что Visual C ++ использует округление «до половины от нуля».

У Рика есть выдающаяся статья по теме , тоже.

ЕслиВы хотели бы поиграть с другими числами и их представлениями с плавающей точкой, вот очень полезный калькулятор IEEE-754 *1025*.

2 голосов
/ 17 февраля 2012

Это полностью зависит от компилятора. Проверьте это в GCC. Это должно быть ххх.12

2 голосов
/ 17 февраля 2012

Попробуйте напечатать значение std::numeric_limits<float>::digits10. Это примерно говорит о том, сколько точности в базе 10 имеет поплавок. Вы пытаетесь превзойти его, поэтому вы испытываете потерю точности (это означает, что цифры за значащими на самом деле не имеют смысла).

См. Например Что означает значение numeric_limits :: цифры10

...