Я полагался на 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
на результат).