PHP:将任何浮点设置为十进制扩展格式
我想创建一个函数PHP:将任何浮点设置为十进制扩展格式,php,floating-point,floating-point-conversion,Php,Floating Point,Floating Point Conversion,我想创建一个函数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
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将它们呈现为而不是
显然是要尝试的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"
对于小浮点数,困难在于选择要求的小数位数。一种方法是要求输入大量十进制数字,然后输入多余的0
s。但是,这种想法是有缺陷的,因为十进制扩展通常不会以0
s结尾:
number_format(1.000001, 30); // "1.000000999999999917733362053696"
number_format(1.000001E-10, 30); // "0.000000000100000099999999996746"
number_format(1.000001E-11, 30); // "0.000000000010000010000000000321"
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;
}
问题是浮点数具有,并且通常无法存储文字的精确值(例如:1.0E+25
)。相反,它存储可以表示的最接近的可能值<代码>数字格式()揭示了这些“最接近的近似值”
Timo-Frenay解
我在页面的深处发现,令人惊讶的是没有投票:
以下是如何打印16位有效数字的浮点数,而不考虑大小:
关键部分是使用来确定浮点数,然后计算所需的小数位数
有几个bug需要修复:
- 该代码不适用于负浮动
- 该代码不适用于非常小的浮动(例如:
)。PHP报告此通知:“1.0E-100
:请求的116位精度被截断为PHP最大53位”sprintf()
- 如果
为$value
,则0.0
为log10($value)
-INF
- 由于a的精度是“大约14位十进制数字”,我认为应该显示14位有效数字,而不是16位
0
s:
number_format(1.000001, 30); // "1.000000999999999917733362053696"
number_format(1.000001E-10, 30); // "0.000000000100000099999999996746"
number_format(1.000001E-11, 30); // "0.000000000010000010000000000321"
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个随机浮动。对于小于约1.0E+15
的所有浮动,该代码似乎都能正常工作
有趣的是,number\u 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"
是否有一种优雅的方法来改进代码以解决此问题?这段代码。我不认为我能使它比你的更优雅,但我花了太多时间在它上面,我不能把它扔掉:)
函数格式浮点(
$value,
$noOfDigits=14,
$separator=',',
$decimal='.'
) {
$exponent=floor(log10(abs$value));
$量级=功率(10$指数);
//提取有效数字
$尾数=(字符串)绝对值(圆形($value/pow(10,$index-$noOfDigits+1));
$formattedNum='';
如果($index>=0){//if($value>=1)
//只是为了预先格式化
$formattedNum=number\u格式($value,$noOfDigits-1,$decimal,$separator);
//然后将$尾数中的数字报告到$formattedNum中
$formattedLen=strlen($formattedNum);
$尾数=strlen($尾数);
对于($fnPos=0,$mPos=0;$fnPos<$formattedLen;$fnPos++,$mPos++){
//跳过非数字
而($formattedNum[$fnPos]==$separator | |$formattedNum[$fnPos]===$decimal | | |$formattedNum[$fnPos]===='-')){
$fnPos++;
}
$formattedNum[$fnPos]=$mPos<$尾数?$尾数[$mPos]:“0”;
}
}else{//if($value<1)
//如有必要,请预先添加减号
如果($value<0){
$formattedNum='-';
}
$formattedNum.=“0”。$decimal.str_repeat('0',abs($exponent)-1)。$尾数;
}
//带尾随的十进制零
$formattedNum=preg\u replace('/\.?0*$/',''$formattedNum);
返回$formattedNum;
}
我成功地创建了这个(相当不雅观的)解决方案
function formatFloat($value)
{
$phpPrecision = 14;
if ($value == 0.0) return '0.0';
if (log10(abs($value)) < $phpPrecision) {
$decimalDigits = max(
($phpPrecision - 1) - floor(log10(abs($value))),
0
);
$formatted = number_format($value, $decimalDigits);
// Trim excess 0's
$formatted = preg_replace('/(\.[0-9]+?)0*$/', '$1', $formatted);
return $formatted;
}
$formattedWithoutCommas = number_format($value, 0, '.', '');
$sign = (strpos($formattedWithoutCommas, '-') === 0) ? '-' : '';
// Extract the unsigned integer part of the number
preg_match('/^-?(\d+)(\.\d+)?$/', $formattedWithoutCommas, $components);
$integerPart = $components[1];
// Split into significant and insignificant digits
$significantDigits = substr($integerPart, 0, $phpPrecision);
$insignificantDigits = substr($integerPart, $phpPrecision);
// Round the significant digits (using the insignificant digits)
$fractionForRounding = (float) ('0.' . $insignificantDigits);
$rounding = (int) round($fractionForRounding); // Either 0 or 1
$rounded = $significantDigits + $rounding;
// Pad on the right with zeros
$formattingString = '%0-' . strlen($integerPart) . 's';
$formatted = sprintf($formattingString, $rounded);
// Insert a comma between every group of thousands
$formattedWithCommas = strrev(
rtrim(
chunk_split(
strrev($formatted), 3, ','
),
','
)
);
return $sign . $formattedWithCommas;
}
如果浮点值小于1.0E+14
,则它使用我问题中的“最佳尝试”代码。否则,它将整数部分舍入为14位有效数字
有500个随机浮点数,代码似乎对所有浮点数都正常工作
正如我所说,这不是一个非常优雅的实现,所以我仍然非常有兴趣知道是否有人能够设计出更好的解决方案
function formatFloat($value)
{
$phpPrecision = 14;
if ($value == 0.0) return '0.0';
if (log10(abs($value)) < $phpPrecision) {
$decimalDigits = max(
($phpPrecision - 1) - floor(log10(abs($value))),
0
);
$formatted = number_format($value, $decimalDigits);
// Trim excess 0's
$formatted = preg_replace('/(\.[0-9]+?)0*$/', '$1', $formatted);
return $formatted;
}
$formattedWithoutCommas = number_format($value, 0, '.', '');
$sign = (strpos($formattedWithoutCommas, '-') === 0) ? '-' : '';
// Extract the unsigned integer part of the number
preg_match('/^-?(\d+)(\.\d+)?$/', $formattedWithoutCommas, $components);
$integerPart = $components[1];
// Split into significant and insignificant digits
$significantDigits = substr($integerPart, 0, $phpPrecision);
$insignificantDigits = substr($integerPart, $phpPrecision);
// Round the significant digits (using the insignificant digits)
$fractionForRounding = (float) ('0.' . $insignificantDigits);
$rounding = (int) round($fractionForRounding); // Either 0 or 1
$rounded = $significantDigits + $rounding;
// Pad on the right with zeros
$formattingString = '%0-' . strlen($integerPart) . 's';
$formatted = sprintf($formattingString, $rounded);
// Insert a comma between every group of thousands
$formattedWithCommas = strrev(
rtrim(
chunk_split(
strrev($formatted), 3, ','
),
','
)
);
return $sign . $formattedWithCommas;
}
函数formatFloat($value)
{
$phpPrecision=14;
如果($value==0.0),则返回“0.0”;
if(对数10(绝对值($value))<$phpPrecision){
$decimalDigits=最大值(
($phpPrecision-1)-地板(log10(abs($value)),
0
);
$formatted=数字\格式($value,$decimalDigits);
//修剪多余的0
$formatted=preg_replace('/(\.[0-9]+?)0*$/','$1',$formatted);
返回$formatted;
}
$formattedWithoutCommas=number_格式($value,0,'.'','');
$sign=(strpos($formattedwithout逗号,'-')==0)?'-':'';
//提取数字的无符号整数部分
预匹配('/^-?(\d+)(\.\d+?$/'),不带逗号的$formatted,$components);
$integerPart=$components[1];
//分成有意义和无意义的数字
$significtdigits=substr($integerPart,0,$phpPrecision);
$insignificantDigits=substr($integerPart,$phpPrecision);
//将有效数字四舍五入(使用不重要的数字)
$fractionForRounding=(浮点)('0..$insignificantDigits);
$rounding=(int)round($fractionForRounding);//0或1
$rounded=$significtdigits+$rounding;
//在右边用零填充
$formattingString='%0-'。strlen($integerPart)。's';
$formatted=sprintf($formattingString,$rounded);
//在每组数千人之间插入逗号
$forma