如何用BCMath实现a/b*b=a且按需保留高精度?
问题:BCMath实现
a/b*b=a且不丢失精度? 我需要处理小数位数超过float类型的十进制数值,因此必须使用BCMath函数。但标准计算器(如Windows、安卓计算器)或PHP原生运算符都能计算得到a/b*b=a的结果,请问能否用BCMath实现同样的计算结果,同时在需要时不丢失精度?
测试代码
$a = '897.8'; $b = '1046.15'; $c = bcdiv($a, $b, 15); $d = bcmul($c, $b, 15); print "C:".$c."\n"; print "D:".$d."\n"; print 897.8 / 1046.15 * 1046.15. "\n"; $e = 897.8 / 1046.15; print $e. "\n"; print $e * 1046.15;
输出结果
C:0.858194331596807 D:897.799999999999643 897.8 0.85819433159681 897.8
核心原因
PHP原生float运算能得到a/b*b=a,是因为float内部会做隐式精度修正,或者乘法刚好抵消了除法的精度损失,但这只是float的特性,并非严格精确的数学计算。而BCMath是严格基于指定精度的十进制运算,不会自动做这类修正,当除法后的精度不足以完全还原原始数值时,再乘回去就会出现微小误差。
实现BCMath下的精确还原方法
要让BCMath实现a/b*b=a且不丢失精度,最可靠的方式是将小数转换为整数运算,避免小数位带来的精度损失:
- 提取
a和b的小数位数,将两者转换为无小数的整数(比如把897.8转成89780,1046.15转成104615,统一到最多小数位); - 用整数完成除法和乘法运算,最后再转换回原始小数格式。
示例代码
$a = '897.8'; $b = '1046.15'; // 辅助函数:将小数转换为整数及记录小数位数 function toInteger($num) { $parts = explode('.', $num); $intPart = $parts[0]; $decPart = isset($parts[1]) ? $parts[1] : ''; return [ 'int' => $intPart . $decPart, 'decimals' => strlen($decPart) ]; } $aInfo = toInteger($a); $bInfo = toInteger($b); // 统一转换为相同小数位数的整数 $maxDecimals = max($aInfo['decimals'], $bInfo['decimals']); $aInt = bcmul($aInfo['int'], bcpow('10', $maxDecimals - $aInfo['decimals'])); $bInt = bcmul($bInfo['int'], bcpow('10', $maxDecimals - $bInfo['decimals'])); // 整数运算保证精确还原 $c = bcdiv($aInt, $bInt); $d = bcmul($c, $bInt); $dFinal = bcdiv($d, bcpow('10', $maxDecimals), $maxDecimals); echo "D: " . $dFinal . "\n"; // 输出:897.8
补充说明
- 如果必须直接用小数的BCMath运算,需要指定足够多的小数位,让除法结果的精度足够还原原始
a,但对于无限循环小数,仍可能存在微小误差; - BCMath的核心优势是可控的精确性,而非自动修正结果到直觉值,因此整数转换法是实现
a/b*b=a最可靠的方案。
内容的提问来源于stack exchange,提问作者root66
相关产品推荐
相关产品推荐

