Я хочу отформатировать число с минимальным количеством знаков после запятой, необходимое для его воспроизведения.
PHP имеет number_format()
функция для рендеринга числа с указанным количеством десятичных знаков. Однако, если я использую его для форматирования 0.1 с очень большим количеством десятичных знаков, я получу:
print rtrim(number_format(0.1, 1000, '.', ''), '0');
// 0.1000000000000000055511151231257827021181583404541015625
поскольку (float)"0.1" === 0.1
те лишние 55511151...
десятичные дроби после позиции 16 бесполезны.
Я могу использовать цикл, как это:
function format_float($float) {
$decimals = 1;
do {
$result = number_format($float, $decimals, '.', '');
$decimals++;
} while ((float)$result !== $float);
return $result;
}
print format_float(0.1) . "\n"; // 0.1
print format_float(1/3) . "\n"; // 0.3333333333333333
print format_float(1E-50) . "\n"; // 0.00000000000000000000000000000000000000000000000001
Но наверняка есть более простой и эффективный способ?
Правильная печать минимального числа десятичных цифр двоичного числа с плавающей запятой очень сложное усилие. Современное состояние семейство алгоритмов грису. Для хорошего объяснения проблем, см. Классическую статью Стил и Уайт.
Вот что я придумал:
function format_float($num) {
$dec = $num == 0 ? 0 : ceil(-log10(abs($num)));
$dec = max(1, $dec + 15 /* magic number */);
$res = number_format($num, $dec, '.', '');
// sometimes we need one more decimal
if ((float)$res !== $num) {
$res = number_format($num, $dec + 1, '.', '');
}
list($l, $r) = explode('.', $res, 2);
return "$l." . (rtrim($r) ?: '0');
}
Предполагается, что необходимое количество десятичных знаков будет 15 - log10($num)
или же 16 - log10($num)
, который, кажется, имеет место на практике согласно моим испытаниям. Это по крайней мере более эффективно, чем моя грубая петля силы.