Непредсказуемое поведение спецификатора% f в c

Со следующим кодом:

float f = 23.456; printf("%f", f); 

Выход для разных значений f :

  Value Output --------- -------- f = 23.456 23.455999 f = 23.956 23.955999 f = 23.947 23.947001 f = 23.656 23.656000 

Почему полученная стоимость непредсказуема или существует шаблон?

Это связано с 32-битным форматом с плавающей точкой IEEE 754, который используется C для хранения чисел с плавающей запятой. Mantissa, который у вас есть, преобразуется в двоичном формате и хранится в младших 23 бит 4-байтового пространства, требуемого переменной float . Иногда он полностью конвертируется в точный двоичный код, в других случаях он не может быть точно преобразован, поэтому сохраняется усеченная форма номера, которая при чтении из памяти оценивается до немного другого значения.

Такое поведение аналогично тому, как в математике вы используете значение 1/3 как 0.33 или 0.333 в зависимости от вашего требования точности.

Попробуйте использовать:
f = 0.25; или же,
f = -15.625; или любое десятичное значение, точно конвертируемое в двоичное, вы получите правильный результат.

Это относится к категории вещей, которые каждый программист должен знать о плавающей запятой. Способ работы с плавающей запятой означает, что некоторые числа не могут быть полностью представлены в плавающей запятой. Самый простой способ объяснить это – попросить вас записать значение 1/3 как десятичное число. Вы начинаете довольно счастливо, но в итоге у вас заканчивается бумага. Причиной этого является то, что в десятизначных обозначениях число 1/3 бесконечно длинное, и поэтому в разумной системе кодирования, которая использует нотацию базы 10 для хранения номера, существует ограничение на то, как долго это может быть.

Плавающая точка делает то же самое, но использует базу 2. Это означает, что числа, которые кажутся нам очень простыми, например 1/10 или 0,1, становятся бесконечно рекурсивными. Это приводит к ошибке округления, когда вы приходите печатать цифры, просто потому, что номер, который вы сохранили, не является номером, который вы дали компилятору, потому что было невозможно сохранить номер.

Каноническая статья по этому поводу – http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html , которая является здоровым чтением, но объясняет все это и многое другое.

С float или double вас есть определенное количество бит, чтобы представить ваш номер x на компьютере. Для float обычно 32 бит, а double – 64 бита.

В результате этого? Вы можете хранить только 2 ^ 32 разных номера в float и 2 ^ 64 в double . Поскольку существует бесконечное количество «других» чисел, это означает, что они не могут быть представлены. Но люди все еще хотят использовать эти числа (ошибка компилятора об неиспользовании числа будет нечетной, не так ли?). Поэтому выбирается число, близкое к x . В большинстве приложений это «достаточно близко». Но по той же причине: Никогда не доверяйте float , т. Е. Никогда не пытайтесь спросить «это x = 5?».