Почему переменная плавания сохраняет значение, разрезая цифры за точкой странным образом?
-
27-10-2019 - |
Вопрос
У меня есть эта простая строка кода:
float val = 123456.123456;
Когда я печатаю этот VAL или смотрю по объему, он хранит значение 123456.13
Хорошо, все в порядке, он не может хранить все эти цифры за точкой только в 4 байтах, но почему это делает 13 после этого? Разве это не должно быть 12?
(Используя VC ++ 2010 Express на Win32)
Решение
При представлении в качестве поплавка, ваш номер имеет показатель 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.
Другие советы
В двоичном изображении 123456.123456 - 11110001001000000.000111111001 ... (Infinite). Он окружает 11110001001000000.001, или 123456.125. Что Раунды до 123456.13 при печати.
Значение, хранящееся в val
равно 123456.125
. Анкет Вы получаете .13
Потому что ты округляешь это:
float val = 123456.123456;
printf("%.4f %.2f\n", val, val);
выход: 123456.1250 123456.13
В этом случае вы должны использовать Double, чтобы избежать усечения. Компилятор также должен предупредить вас: «Предупреждение C4305:« Инициализация »: усечение от« двойного »до« float »».
Попробуйте печатать значение std::numeric_limits<float>::digits10
. Анкет Это грубо говоря, сколько точности в основании 10 плавает. Вы пытаетесь превзойти его, поэтому вы испытываете потерю точности (что означает, что цифры за пределами значимых не совсем значимы).
Смотрите, например В чем смысл numeric_limitsu003Cdouble> :: Digits10
Это полностью зависит от компилятора. Проверьте это в GCC. Это должно быть xxx.12