fab2s/math

基于10的基高精度数学助手

2.0.0 2024-04-23 22:26 UTC

This package is auto-updated.

Last update: 2024-09-23 23:24:59 UTC


README

CI QA codecov Total Downloads Monthly Downloads Latest Stable Version PRs Welcome License

一个基于 bcmath 的流畅的 助手,用于以相当严格的方法处理10进制中的高精度计算(对精度有要求吗?)。它不会试图变得聪明,如果没有 bcmath,就直接失败,但它会自动检测 GMP 以实现更快的基转换。

Bcmath 支持任何大小和精度的数字,精度最高可达 2,147,483,647(或 0x7FFFFFFF)位十进制,如果内存足够,则表示为字符串。

安装

可以使用 composer 安装 Math

composer require "fab2s/math"

Math 也包含在 OpinHelper 中,该包包含几个“瑞士军刀”级别以下的小助手,涵盖了PHP编程中一些最令人烦恼的方面,例如UTF8字符串操作、高精度数学或正确锁定文件

先决条件

Math 需要 bcmathGMP 会自动检测并在可用时用于更快的基础转换(最多 62)。

实际使用

由于 Math 的目的是用于精度要求高的场合,因此它在输入数字方面非常严格:在通过 trim() 后,如果输入数字不匹配 ^[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+)$,它将抛出异常。

实际上这意味着“-.0051”和“00028.34”是可接受的,但“1E12”、“3,14”或“1.1.1”将抛出异常。这样做的原因是,在 bcmath 的世界里,“1E12”、“1.1.1”和“abc”都是“0”,如果你不做任何事情,这可能会导致一些灾难。

一个 Math 实例仅初始化为有效的10进制数字。从那里你可以进行数学计算,并将实例强制转换为字符串以在任何阶段获取当前结果。

// instance way
$number = new Math('42');

// fluent grammar
$result = (string) $number->add('1')->sub(2)->div(1)->add(1)->mul(-1); // '-42'

// factory way: number
$result = (string) Math::number('42')->add('1')->sub(2)->div('1')->add(1)->mul(-1); // '-42'

// factory way: fromBase
$result = (string) Math::fromBase('LZ', 62); // '1337'
$result = (string) Math::fromBase('LZ', 62)->sub(1295); // '42'

// combos
$number = Math::number('42')
    ->add(Math::fromBase('LZ', 62), '-42')
    ->sub('1337', '42')
    ->mul(3, 4, 1)
    ->div(4, 3)
    ->sub('.1')
    ->abs()
    ->round(0)
    ->floor()
    ->ceil()
    ->min('512', '256')
    ->max('8', '16', '32');

// formatting does not mutate internal number
$result = (string) $number->format(2); // '42.00'
$result = (string) $number; // '42';
// and you can continue calculating after string cast
$result = (string) $number->add('1295')->toBase(62); // 'LZ'

// toBase does not mutate base 10 internal representation
$result = (string) $number; // '1337';

任何此类计算的字符串形式都是归一化的(例如,'-0'、'+.0' 或 '0.00' 转换为 '0'),这意味着您可以准确地比较 Math 实例的结果

$result = (string) Math::number('0000042.000000'); // '42'

// raw form
$result = Math::number('0000042.000000')->getNumber(); // '0000042.000000'

// with some tolerance
$result = Math::number('  42.0000 ')->getNumber(); // '42.0000'

// at all time
if ((string) $number1 === (string) $number2) {
    // both instance numbers are equals
}

// same as (internally using bccomp)
if ($number1->eq($number2)) {
    // both instance numbers are equals
}

您可以在计算时直接重用部分 $calculus 作为实例

$number = new Math('42');
// same as
$number = Math::number('42');

// in constructor
$result = (string) (new Math($number))->div('2'); // '21'
// same as
$result = (string) Math::number($number)->div('2'); // '21'

// in calc method
$result = (string) Math::number('42')->add($number)->sub('42')->div('2'); // '21'

这样做实际上比将现有的实例强制转换为字符串要快,因为它不会触发归一化(内部数字状态仅在导出结果时归一化)也不进行数字验证,因为内部 $number 在所有时候都是有效的。

参数应该是字符串或 Math,但可以使用整数,直到 INT_(32|64)

您不应该 使用 floats,因为将它们转换为 string 可能会导致本地依赖的格式,例如使用逗号而不是点作为小数点或转换为指数表示法,这是 bcmath 不支持的。浮点数在一般处理方式和 PHP 特殊处理方式是 bcmath 存在的原因,因此即使您信任您的区域设置,使用浮点数也会在一定程度上违背使用此类库的目的。

内部精度

精度处理不依赖于 bcscale,因为它在现实生活中并不可靠。由于它是一个全局设置,它可能会影响或被远离/无关的代码影响(在 fpm 中它实际上可以传播到所有 PHP 进程)。

Math 在实例和全局(限于当前PHP进程)精度下处理精度。全局精度存储在静态变量中。当设置后,每个新的实例将以此全局精度作为自己的精度(实例化后仍可设置实例精度)。当未设置全局精度时,初始实例精度默认为 Math::PRECISION(目前为9,或小数点后9位)

// set global precision
Math::setGlobalPrecision(18);

$number = (new Math('100'))->div('3'); // uses precision 18
$number->setPrecision(14); // will use precision 14 for any further calculations

Laravel

对于使用 Laravel 的用户,Math 提供了 Laravel 扩展器:MathCaster,您可以使用它直接转换模型属性。

use fab2s\Math\Laravel\MathCast;

class MyModel extends Model
{
    protected $casts   = [
        'not_nullable' => MathCast::class,
        'nullable'     => MathCast::class . ':nullable',
    ];
}

$model = new MyModel;

$model->not_nullable = 41;
$model->not_nullable->add(1)->eq(42); // true

$model->not_nullable = null; // throw a NotNullableException

$model->nullabe = null; // is ok

需求

Math 已在 php 8.1 和 8.2 上进行了测试。此外,MathCast 已在 Laravel 10 和 11 上进行了测试。

贡献

欢迎贡献,请随时打开问题并提交拉取请求。

许可证

Math 是开源软件,使用 MIT 许可证 许可。