我希望使用重现它所需的最小小数位数来格式化浮点数。
PHP具有number_format()
函数,用于呈现具有指定小数位数的数字。但是,如果我使用它来格式化具有非常高的小数位数的0.1,我得到:
print rtrim(number_format(0.1, 1000, '.', ''), '0');
// 0.1000000000000000055511151231257827021181583404541015625
自(float)"0.1" === 0.1
起,第16位后的额外55511151...
小数无效。
我可以使用循环,如下所示:
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
但肯定有一种更简单,更有效的方法吗?
答案 0 :(得分:1)
正确地打印二进制浮点数的最小十进制数是非常复杂的努力。目前最先进的是grisu family of algorithms。有关所涉问题的详细解释,请参阅Steele and White的经典论文。
答案 1 :(得分:1)
@Jesse的代码对我不起作用,因此构建此代码:
function formatFloat(float $num, int $dec = 0) : string {
// format to maximum decimal places, and make positive
$abs = number_format(abs($num), 17, '.', '');
// is it less than 0?
if (!floor($abs)) {
// look through each number until we find one that is not 0
foreach (str_split(substr($abs, 2)) AS $i => $item) {
if ($item) {
break;
}
}
// add defined decimal places
$dec += $i + 1;
}
// format the output
return number_format($num, $dec);
}
它可以处理大于1的数字(负数),并允许您指定找到第一个有效数字后将浮点格式设置为多少小数位。它也表现更好。
答案 2 :(得分:0)
这就是我提出的:
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)
,根据我的测试,这似乎在实践中有效。它至少比我的暴力循环效率更高。