Я полагался на sprintf('%0.1f', 2.25) === '2.3'
но, оказывается, он приходит в 2.2
!
На самом деле это кажется случайным:
php > for ($j=0;$j<=10;$j++) { printf( "%s -> %0.1f\n",$j+ 0.05, $j+0.05); } 0.05 -> 0.1 // Up, as expected 1.05 -> 1.1 // Up, as expected 2.05 -> 2.0 // Down! 3.05 -> 3.0 // Down! 4.05 -> 4.0 // Down! 5.05 -> 5.0 // Down! 6.05 -> 6.0 // Down! 7.05 -> 7.0 // Down! 8.05 -> 8.1 // Up, as expected 9.05 -> 9.1 // Up, as expected
Я полностью упустил момент? Я чувствую, что из-под меня убирают коврик и что все, что я узнал в школе, ошибается …! Разумеется, функция округления чисел должна делать это последовательно? (Я отмечаю, что round($n, 1)
работает так, как ожидалось.)
В качестве объяснения того, почему round
может предложить лучшие результаты, чем функция, специально не предназначенная для округления, нам нужно рассмотреть пределы представления с плавающей запятой двойной точности. Партии могут представлять от 15 до 17 значащих значащих цифр. Из статьи Википедии о двойной точности :
Если десятичная строка длиной не более 15 значащих цифр преобразуется в представление двойной точности IEEE 754, а затем преобразуется обратно в строку с таким же количеством значащих цифр, то окончательная строка должна соответствовать оригиналу. Если двойная точность IEEE 754 преобразуется в десятичную строку с не менее 17 значащими цифрами, а затем преобразуется обратно в двойную, то окончательное число должно совпадать с оригиналом
Реализация round
может и должна использовать это, чтобы «делать правильные вещи» в большинстве случаев.
Пример 1:
<?=number_format(2.05,14); //give me 15 significant digits. Guaranteed to produce the orginal number
выходы:
2.05000000000000
Пример 2:
<?=number_format(2.05,16); //give me 17 significant digits. Not guaranteed to produce the orginal number
выходы:
2.0499999999999998
Это всего лишь демонстрация поведения IEEE 754.
Я собираюсь угадать (потому что я не читал его реализации), что sprintf
самом деле не пытается сделать что-то особенно интеллектуальное в отношении округления, тогда как round
вероятно, пытается округлить «правильно» (по IEEE 754) относительно количество важных цифр, которые вы просили.
Вы должны использовать round
http://php.net/manual/en/function.round.php
round(2.25, 2) === floatval('2.3')
Подводя итог тому, что было сказано в комментариях в ответе:
Потому что printf
не является функцией округления , а функцией низкого уровня, чтобы дать строковое представление числа. В этом случае число внутри не совпадает с номером, например, оно может быть 2.249999991231231123
из-за ограничений внутреннего представления с плавающей запятой.
Таким образом, printf
округляет другое число до того, что вы ввели / вычислено, что в этом примере правильно 2.2
.
Поэтому, как указывает другой ответ (и мой первоначальный вопрос), лучшим решением является использование round()
(и, возможно, sprintf
для результата).