使用BC数学函数解决小数运算精度问题

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
ini_set('error_reporting', E_ALL);

/**
 * BC数学函数
 * 说明1:目前只支持+()-()*()/()===(等于)>(大于)<(小于)这几种操作,若要增加支持>=(大于等于)%(取模)^(n次方)等操作,可自行扩展。
 * 说明2:涉及金钱或任意一边操作数为浮点数的计算都应该始终优先使用BC数学函数,避免出现精度问题。
 *
 * @param mixed $num1 左操作数
 * @param string $operator 操作符,可选值:+()-()*()/()===(等于)>(大于)<(小于)
 * @param mixed $num2 右操作数
 * @return float|bool|null 若是加减乘除运算则返回float类型,若是判等则返回bool类型,其它目前暂未支持的操作则返回null
 */
function bc_math(mixed $num1, string $operator, mixed $num2): float|bool|null
{
    $scale = 2;

    $num1 = (string)$num1;
    $num2 = (string)$num2;

    switch ($operator) {
        case '+':
        {
            $result = (float)bcadd($num1, $num2, $scale);
            break;
        }
        case '-':
        {
            $result = (float)bcsub($num1, $num2, $scale);
            break;
        }
        case '*':
        {
            $result = (float)bcmul($num1, $num2, $scale);
            break;
        }
        case '/':
        {
            $result = (float)bcdiv($num1, $num2, $scale);
            break;
        }
        case '===':
        {
            $result = bccomp($num1, $num2, $scale) === 0;
            break;
        }
        case '>':
        {
            $result = bccomp($num1, $num2, $scale) === 1;
            break;
        }
        case '<':
        {
            $result = bccomp($num1, $num2, $scale) === -1;
            break;
        }
        default:
            $result = null;
    }

    return $result;
}

var_dump(0.1 + 0.7); // float(0.7999999999999999)
var_dump(1.0 - 0.9); // float(0.09999999999999998)
var_dump(0.1 * 0.2); // float(0.020000000000000004)
var_dump(0.7 / 0.1); // float(6.999999999999999)
var_dump((0.1 + 0.2) === 0.3); // bool(false)
var_dump((0.1 + 0.2) > 0.3); // bool(true)
var_dump(0.3 < (0.1 + 0.2)); // bool(true)

var_dump(bc_math(0.1, '+', 0.7)); // float(0.8)
var_dump(bc_math(1.0, '-', 0.9)); // float(0.1)
var_dump(bc_math(0.1, '*', 0.2)); // float(0.02)
var_dump(bc_math(0.7, '/', 0.1)); // float(7)
var_dump(bc_math((0.1 + 0.2), '===', 0.3)); // bool(true)
var_dump(bc_math((0.1 + 0.2), '>', 0.3)); // bool(false)
var_dump(bc_math(0.3, '<', (0.1 + 0.2))); // bool(false)

Copyright © 2025 码农人生. All Rights Reserved