我想创建一个函数formatFloat(),它接受任何浮动并将其格式化为十进制扩展字符串.例如:
formatFloat(1.0E+25); // "10,000,000,000,000,000,000,000,000"
formatFloat(1.0E+24); // "1,000,000,000,000,000,000,000,000"
formatFloat(1.000001); // "1.000001"
formatFloat(1.000001E-10); // "0.0000000001000001"
formatFloat(1.000001E-11); // "0.00000000001000001"
初步想法
只要铸造浮子字符串都不行,因为花车比约大1.0E+14,或比约较小1.0E-4,PHP使得他们在科学记数法,而不是十进制扩展.
number_format()是尝试的明显的PHP功能.但是,大浮动会出现此问题:
number_format(1.0E+25); // "10,000,000,000,000,000,905,969,664"
number_format(1.0E+24); // "999,999,999,999,999,983,222,784"
对于小浮点数,难点在于选择要求的小数位数.一个想法是要求大量的十进制数字,然后rtrim()是多余0的数字.但是,这个想法是有缺陷的,因为十进制扩展通常不会以0s 结尾:
number_format(1.000001, 30); // "1.000000999999999917733362053696"
number_format(1.000001E-10, 30); // "0.000000000100000099999999996746"
number_format(1.000001E-11, 30); // "0.000000000010000010000000000321"
问题是浮点数的精度有限,通常无法存储文字的确切值(例如:) 1.0E+25.相反,它存储可以表示的最接近的可能值. number_format()揭示了这些"最接近的近似值".
Timo Frenay的解决方案
我发现这个评论深埋在sprintf()页面中,令人惊讶的是没有任何赞成票:
以下是如何使用16位有效数字打印浮点数,无论大小如何:
$result = sprintf(sprintf('%%.%dF', max(15 - floor(log10($value)), 0)), $value);
关键部分是使用log10()确定浮点数的数量级,然后计算所需的小数位数.
有一些需要修复的错误:
该代码不适用于负浮点数.
该代码不适用于极小的浮点数(例如:) 1.0E-100.PHP报告此通知:" sprintf():116位数的请求精度被截断为PHP最多53位数"
如果$value是0.0,那log10($value)就是-INF.
由于PHP浮点数的精度是"大约14位小数",我认为应该显示14位有效数字而不是16位数.
我最好的尝试
这是我提出的最佳解决方案.它基于Timo Frenay的解决方案,修复了错误,并使用ThiefMaster的正则表达式来修剪多余的0s:
function formatFloat($value)
{
if ($value == 0.0) return '0.0';
$decimalDigits = max(
13 - floor(log10(abs($value))),
0
);
$formatted = number_format($value, $decimalDigits);
// Trim excess 0's
$formatted = preg_replace('/(\.[0-9]+?)0*$/', '$1', $formatted);
return $formatted;
}
这是一个包含200个随机浮点数的Ideone演示.代码似乎适用于小于约的所有浮点数1.0E+15.
有趣的是number_format(),即使是非常小的浮动也能正常工作:
formatFloat(1.000001E-250); // "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001"
这个问题
我最好的尝试formatFloat()仍然遇到这个问题:
formatFloat(1.0E+25); // "10,000,000,000,000,000,905,969,664"
formatFloat(1.0E+24); // "999,999,999,999,999,983,222,784"
有没有一种优雅的方法来改进代码来解决这个问题?