Почему переменная плавания сохраняет значение, разрезая цифры за точкой странным образом?

StackOverflow https://stackoverflow.com/questions/9328854

Вопрос

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top