编程

PHP 8.4: BCMath: 新增 bcdivmod 函数

168 2024-11-26 03:50:00

PHP 8.4 中的 BCMath 扩展引入了一个名为 bcdivmod 的新函数,该函数将给定的数字除以给定的除数,并返回商和余数。结果与调用 bcdivbcmod 函数基本相同。

bcdivmod 可用于需要商和余数的用例,它避免了必须分别调用 bcdivbcmod 函数。

返回值是一个数组,第一个值是商,第二个值是余数。

bcdivmod('42', '10');
// ["4", "2"]

bcdivmod 函数摘要

/**
 * @param string $num1 Dividend, as a string.
 * @param string $num2 Divisor, as a string.
 * @param int|null $scale Number of digits after the decimal place in the
 *  remainder. If omitted or null, it will default to the scale set globally
 *  with the bcscale() function, or fallback bcmath.scale INI value
 *  (default to 0) if this has not been set.
 *
 * @return array
 */
function bcdivmod(string $num1, string $num2, ?int $scale = null): array {}
  • bcdivmod 在全局命名空间中声明
  • 如果 scale 值为 null,则它将继承使用 bcscale 函数设置的 scale 值,并回退到 bcmath.scale INI 值。
  • 返回值永远是数组。
  • $num2 参数传入 0 将会抛出 DivisionByZeroError 异常。
  • $num1$num2 参数传递非数字字符串会引发 ValueError 异常,指出该值格式不正确。

bcdivmod 返回一个数组,其中商(整数值)为字符串,余数为包含 $scale 个十进制值的字符串。

用例

bcdivmod("10", "10");
// ["1", "0"]
bcdivmod("10", "100");
// ["0", "10"]
// Using default bcmath.scale INI value = 0
bcdivmod("8957", "5.43242"); 
// ["1648", "4"]
// Setting scale value
bcdivmod("8957", "5.43242", 10); 
// ["1648", "4.3718400000"]
bcdivmod("8957.5454312", "5.43242", 10);
// ["1648", "4.9172712000"]
bcdivmod("0", 42);
// ["0", "0"]

bcdivmod 错误条件

bcdivmod 函数遵循与 BCMath 扩展中的其他函数类似的语义。传递非数值会引发 ValueError 异常,并且由于 $num1$num2 参数的类型声明是字符串,因此在声明 declare(strict_types=1) 生效时,它遵循标准的 PHP 类型强制和严格的类型规则。

将零作为除数时将抛出  DivisionByZeroError

bcdivmod("42", "0");
DivisionByZeroError: Division by zero

将非数值字符串和 INF 传递给 $num1$num2 将抛出 ValueError

bcdivmod("42", "foobar");
ValueError: bcdivmod(): Argument #2 ($num2) is not well-formed.

向后兼容性影响

bcdivmod 是 PHP 8.4 BCMath 扩展中添加的一个新函数。除非存在同名的现有全局函数,否则此更改不应导致任何向后兼容性问题。

以下是一个使用 bcdivbcmod 函数的 PHP polyfill,与 PHP 8.0 及更高版本兼容:

/**
 * @param string $num1 Dividend, as a string.
 * @param string $num2 Divisor, as a string.
 * @param int|null $scale Number of digits after the decimal place in the
 *  remainder. If omitted or null, it will default to the scale set globally
 *  with the bcscale() function, or fallback to bcmath.scale INI value (default to 0) if this has not been set.
 *
 * @return array
 */
function bcdivmod(string $num1, string $num2, ?int $scale = null): array {
    if (!is_numeric($num1)) {
        throw new \ValueError('Argument #1 ($num1) is not well-formed');
    }

    if (!is_numeric($num2)) {
        throw new \ValueError('Argument #2 ($num2) is not well-formed');
    }

    if ($num2 === '0') {
        throw new \DivisionByZeroError('Division by zero');
    }

    return [
        \bcdiv($num1, $num2, 0),
        \bcmod($num1, $num2, $scale),
    ];
}