PHP高精度数学计算功能bcmath

远昔 代码记录 2025-02-23 829 0

前言:以前写程序用户余额和价格数据,都是写的小数点后最多存储2位数字,这次不同 要精准到5位小数,从decimal(10,2) 干到decimal(10,5) 了,然后惯用的round和floatval函数也不好使了。

round($number, 5)  这样偶尔也不行,只能计算到后四位小数,于是我发现可以用bcmath高精度数学计算,适用于需要处理大数或高精度浮点数的场景。
bcmath 函数使用字符串来表示数字,因此可以避免浮点数精度问题,下方是实例演示:
//加法
$num1 = "1.234567890123456789";
$num2 = "9.876543210987654321";
$result = bcadd($num1, $num2, 10); // 结果保留10位小数
echo $result; // 输出: 11.1111111011


//减法
$num1 = "10.123456789";
$num2 = "5.987654321";
$result = bcsub($num1, $num2, 6); // 结果保留6位小数
echo $result; // 输出: 4.135802


//乘法
$num1 = "2.5";
$num2 = "3.5";
$result = bcmul($num1, $num2, 2); // 结果保留2位小数
echo $result; // 输出: 8.75


//除法
$num1 = "10";
$num2 = "3";
$result = bcdiv($num1, $num2, 8); // 结果保留8位小数
echo $result; // 输出: 3.33333333


//比较俩个数
$num1 = "1.23456789";
$num2 = "1.23456788";
$result = bccomp($num1, $num2, 8); // 比较到8位小数
echo $result; // 输出: 1 ($num1 > $num2)



//针对高精度四舍五入需求的 完美解决方案,提供一个可直接使用的 bcround 函数,支持任意小数位数且无精度损失

// 基本示例
echo bcround('1.234564', 5); // 输出: 1.23456
echo bcround('1.234565', 5); // 输出: 1.23457
echo bcround('-9.999999', 3); // 输出: -10.000 (进位导致整数部分变化)

// 边界测试
echo bcround('0.000004999', 5); // 输出: 0.00000
echo bcround('0.000005', 5); // 输出: 0.00001
echo bcround('123456789.123456789', 8); // 输出: 123456789.12345679

/**
 * 高精度四舍五入函数(支持任意小数位数)
 * @param string|int|float $number 输入数字(建议以字符串形式传入避免精度丢失)
 * @param int $scale 保留的小数位数(必须 >= 0)
 * @return string 四舍五入后的字符串结果
 */
function bcround($number, int $scale = 0): string
{ $number="$number";
    if (!is_numeric($number)) {
        throw new InvalidArgumentException("输入必须是数字");
    }
    if ($scale < 0) {
        throw new InvalidArgumentException("小数位数不能为负数");
    }

    $number = (string)$number; // 强制转换为字符串处理

    // 分离整数和小数部分
    $parts = explode('.', $number);
    $integerPart = $parts[0];
    $decimalPart = isset($parts[1]) ? substr($parts[1], 0, $scale + 1) : ''; // 截取到需要检查的位数+1

    // 若小数位数不足,直接补零返回
    if (strlen($decimalPart) <= $scale) {
        return $scale === 0 ? $integerPart : sprintf("%s.%s", $integerPart, str_pad($decimalPart, $scale, '0'));
    }

    // 获取需要四舍五入的位置
    $roundDigit = (int)$decimalPart[$scale];
    $decimalBase = substr($decimalPart, 0, $scale);

    // 判断是否需要进位
    if ($roundDigit < 5) {
        return $scale === 0 ? $integerPart : sprintf("%s.%s", $integerPart, $decimalBase);
    }

    // 处理进位逻辑
    $carry = '0.' . str_repeat('0', $scale) . '1';
    return bcadd(
        sprintf("%s.%s", $integerPart, $decimalBase),
        $carry,
        $scale
    );
}


评论

发表评论:

挤眼 亲亲 咆哮 开心 想想 可怜 糗大了 委屈 哈哈 小声点 右哼哼 左哼哼 疑问 坏笑 赚钱啦 悲伤 耍酷 勾引 厉害 握手 耶 嘻嘻 害羞 鼓掌 馋嘴 抓狂 抱抱 围观 威武 给力
提交评论

清空信息
关闭评论