fab2s/math
Composer 安装命令:
composer require fab2s/math
包简介
A Base10 high precision math helper
README 文档
README
A fluent, high-precision arithmetic library for PHP built on bcmath. Designed for financial calculations, scientific computing, and anywhere floating-point errors are unacceptable.
The Problem
Floating-point arithmetic has well-known precision limitations:
var_dump((0.1 + 0.7) == 0.8); // false var_dump((1.4 - 1) * 100); // 39.99999999999999 var_dump(0.7 + 0.1 - 0.8); // -1.1102230246251565E-16
bcmathsupports numbers of any size and precision up to 2,147,483,647 decimals, represented as strings.
Installation
composer require fab2s/math
Requirements
- PHP 8.1+
- ext-bcmath (required)
- ext-gmp (optional, faster base conversions, mod, pow and powMod)
Features
Fluent API
Chain operations naturally with variadic argument support:
use fab2s\Math\Math; $result = Math::number('100') ->add('10', '20', '30') // 160 ->mul('2') // 320 ->div('4') // 80 ->sub('38'); // 42 echo $result; // '42'
Math is immutable — every operation returns a new instance, leaving the original unchanged:
$a = Math::number('100'); $b = $a->add('50'); // $a is still '100', $b is '150' $c = $b->mul('2'); // $b is still '150', $c is '300'
The overhead is a single clone per operation (two properties: a string and an int).
Mutable Variant
For performance-sensitive hot loops, MathMutable modifies the instance in place:
use fab2s\Math\MathMutable; $sum = MathMutable::number('0'); for ($i = 0; $i < 1000; $i++) { $sum->add($i . '.99'); // modifies $sum in place, no clone }
MathMutable extends Math, so it is accepted anywhere Math is type-hinted.
Strict Validation
Math rejects ambiguous inputs that bcmath would silently convert to 0:
// Valid Math::number('42'); Math::number('-0.005'); Math::number('.5'); // Throws exception Math::number('1E12'); // Exponential notation Math::number('3,14'); // Comma separator Math::number('$100'); // Currency symbols
Full Arithmetic Operations
$n = Math::number('100'); // Basic $n->add(...$nums); // Addition $n->sub(...$nums); // Subtraction $n->mul(...$nums); // Multiplication $n->div(...$nums); // Division // Advanced $n->sqrt(); // Square root $n->pow('2'); // Power $n->mod('7'); // Modulo $n->powMod($e, $m); // Modular exponentiation $n->abs(); // Absolute value $n->negate(); // Flip sign // Division $n->quotientAndRemainder('7'); // [$quotient, $remainder] // Rounding $n->round(2); // Round to 2 decimals $n->floor(); // Round down $n->ceil(); // Round up // Limits $n->min('50', '200'); // 50 $n->max('50', '200'); // 200 $n->clamp('10', '90'); // Clip between bounds
Comparisons & Inspection
$n = Math::number('42'); $n->eq('42'); // true — equal $n->gt('40'); // true — greater than $n->gte('42'); // true — greater than or equal $n->lt('50'); // true — less than $n->lte('42'); // true — less than or equal $n->isZero(); // false $n->isPositive(); // true $n->isNegative(); // false $n->isEven(); // true $n->isOdd(); // false $n = Math::number('42.99'); $n->getScale(); // 2 $n->getIntegralPart(); // '42' $n->getFractionalPart(); // '99'
Base Conversion (2-62)
Uses GMP when available for faster conversions:
// From base X to base 10 Math::fromBase('LZ', 62); // '1337' Math::fromBase('101010', 2); // '42' Math::fromBase('ff', 16); // '255' (case-insensitive for bases <= 36) // From base 10 to base X Math::number('1337')->toBase(62); // 'LZ' Math::number('42')->toBase(2); // '101010' Math::number('255')->toBase(16); // 'ff' // Negative numbers preserve their sign Math::number('-42')->toBase(16); // '-2a' Math::fromBase('-LZ', 62); // '-1337'
Formatting
Formatting does not mutate the internal number:
$n = Math::number('1234567.891'); echo $n->format(2); // '1234567.89' echo $n->format(2, ',', ' '); // '1 234 567,89' echo $n; // '1234567.891' (unchanged)
Precision Control
Default precision is 9 decimal places. Control it globally or per-instance:
// Global (affects new instances) Math::setGlobalPrecision(18); // Per-instance $n = Math::number('100')->setPrecision(4); echo $n->div('3'); // '33.3333'
Precision is not handled via
bcscale()to avoid global state issues in long-running processes.
Normalized Output
Results are automatically normalized for accurate comparisons:
echo Math::number('0000042.000'); // '42' echo Math::number('-0'); // '0' echo Math::number('+.500'); // '0.5' // Raw access when needed Math::number('0042.00')->getNumber(); // '0042.00'
Instance Reuse
Pass Math instances directly to avoid re-validation:
$tax = Math::number('0.20'); $price = Math::number('99.99'); $total = $price->add($price->mul($tax));
Laravel Integration
Cast Eloquent model attributes to Math instances:
use fab2s\Math\Laravel\MathCast; class Order extends Model { protected $casts = [ 'total' => MathCast::class, 'discount' => MathCast::class . ':nullable', ]; } $order = new Order; $order->total = '99.99'; $order->total->mul('1.2')->format(2); // '119.99' $order->discount = null; // OK (nullable) $order->total = null; // Throws NotNullableException
Mutable Cast
Use MathMutableCast to get MathMutable instances instead of immutable Math:
use fab2s\Math\Laravel\MathCast; use fab2s\Math\Laravel\MathMutableCast; class Order extends Model { protected $casts = [ 'total' => MathMutableCast::class, 'discount' => MathMutableCast::class . ':nullable', 'tax' => MathCast::class, // immutable (default) ]; } $order = new Order; $order->total = '99.99'; $order->total->add('10'); // modifies in place
Using separate cast classes enables proper static type resolution — Larastan/PHPStan will resolve MathCast properties to Math and MathMutableCast properties to MathMutable.
Upgrading from v2
In v2, Math was mutable, so MathCast attributes behaved as mutable values. In v3, Math is immutable by default — existing code that mutates cast attributes in place will silently lose changes:
// v2: works — Math was mutable // v3: $order->total is unchanged — Math is now immutable $order->total->add('10');
To restore the previous behavior, switch to MathMutableCast:
use fab2s\Math\Laravel\MathMutableCast; protected $casts = [ 'total' => MathMutableCast::class, 'discount' => MathMutableCast::class . ':nullable', ];
API Reference
Factory Methods
| Method | Description |
|---|---|
Math::number($n) |
Create immutable instance |
Math::make($n) |
Alias for number() |
Math::fromBase($n, $base) |
Create from base 2-62 |
MathMutable::number($n) |
Create mutable instance |
MathMutable::make($n) |
Alias for number() |
MathMutable::fromBase($n, $base) |
Create mutable from base 2-62 |
Arithmetic
| Method | Description |
|---|---|
add(...$n) |
Addition |
sub(...$n) |
Subtraction |
mul(...$n) |
Multiplication |
div(...$n) |
Division |
quotientAndRemainder($n) |
Returns [$quotient, $remainder] |
mod($n) |
Modulo |
pow($n) |
Power |
powMod($exp, $mod) |
Modular exponentiation |
sqrt() |
Square root |
abs() |
Absolute value |
negate() |
Flip sign |
clamp($min, $max) |
Clip between bounds |
Rounding
| Method | Description |
|---|---|
round($precision) |
Round to precision |
floor() |
Round down |
ceil() |
Round up |
Comparison & Inspection
| Method | Description |
|---|---|
eq($n) |
Equal |
gt($n) |
Greater than |
gte($n) |
Greater than or equal |
lt($n) |
Less than |
lte($n) |
Less than or equal |
min(...$n) |
Minimum value |
max(...$n) |
Maximum value |
isZero() |
Check if zero |
isPositive() |
Check if positive |
isNegative() |
Check if negative |
isEven() |
Check if even integer |
isOdd() |
Check if odd integer |
Conversion & Output
| Method | Description |
|---|---|
toBase($base) |
Convert to base 2-62 |
format($dec, $point, $sep) |
Format with separators |
getNumber() |
Get raw (non-normalized) number |
getScale() |
Number of decimal places |
getIntegralPart() |
Part before the decimal point |
getFractionalPart() |
Part after the decimal point |
(string) |
Get normalized number |
Precision
| Method | Description |
|---|---|
setPrecision($p) |
Set instance precision |
getPrecision() |
Get instance precision |
Math::setGlobalPrecision($p) |
Set default for new instances |
Math::getGlobalPrecision() |
Get global precision |
Benchmarks
Compared against brick/math (PHP 8.4, opcache off, GMP enabled). The bold value is the faster one in each row, and Factor shows how many times faster it is.
| Operation | fab2s/math | brick/math | Factor |
|---|---|---|---|
| instantiate int | 0.261μs (±4.0%) | 0.301μs (±8.9%) | 1.15x |
| instantiate string | 0.244μs (±36.6%) | 0.678μs (±4.5%) | 2.78x |
| add | 0.632μs (±9.7%) | 2.278μs (±3.0%) | 3.60x |
| add variadic | 1.406μs (±5.6%) | 6.653μs (±1.3%) | 4.73x |
| sub | 0.612μs (±1.7%) | 2.325μs (±4.8%) | 3.80x |
| mul | 0.665μs (±2.5%) | 2.183μs (±4.9%) | 3.28x |
| div | 0.762μs (±4.1%) | 4.664μs (±3.6%) | 6.12x |
| pow | 0.986μs (±47.0%) | 1.416μs (±45.5%) | 1.44x |
| mod | 0.851μs (±2.3%) | 2.851μs (±42.2%) | 3.35x |
| sqrt | 2.160μs (±3.6%) | 4.536μs (±11.3%) | 2.10x |
| abs | 0.344μs (±4.0%) | 0.919μs (±35.8%) | 2.67x |
| negate | 0.379μs (±10.5%) | 1.036μs (±93.7%) | 2.73x |
| clamp | 0.956μs (±64.5%) | 4.325μs (±32.5%) | 4.53x |
| quotient & remainder | 0.894μs (±10.7%) | 2.878μs (±4.6%) | 3.22x |
| inspection | 1.943μs (±5.7%) | 4.672μs (±3.7%) | 2.40x |
| round | 0.596μs (±31.8%) | 3.495μs (±10.2%) | 5.86x |
| ceil | 0.528μs (±8.1%) | 2.965μs (±39.6%) | 5.62x |
| floor | 0.469μs (±6.2%) | 2.504μs (±4.0%) | 5.34x |
| comparisons | 1.400μs (±7.5%) | 6.171μs (±4.7%) | 4.41x |
| to string | 0.529μs (±6.6%) | 0.789μs (±3.4%) | 1.49x |
| chained workflow | 1.810μs (±3.2%) | 8.519μs (±2.0%) | 4.71x |
| large number ops | 1.792μs (±5.9%) | 8.273μs (±1.8%) | 4.62x |
| accumulate 100 additions | 41.182μs (±3.0%) | 147.875μs (±11.8%) | 3.59x |
| base convert to 62 | 1.162μs (±23.8%) | 6.888μs (±5.6%) | 5.93x |
| base convert to 16 | 1.081μs (±15.3%) | 0.965μs (±7.6%) | 0.89x |
| integer mul | 0.937μs (±11.6%) | 1.873μs (±5.3%) | 2.00x |
| integer powmod | 1.263μs (±10.0%) | 2.810μs (±7.9%) | 2.22x |
| create 1000 instances | 301.794μs (±4.2%) | 731.200μs (±2.6%) | 2.42x |
All operations above use immutable Math (the default). fab2s/math wins every operation except base-16 conversion, where brick/math delegates to GMP's native hex output. The speed advantage comes from keeping bcmath's C-level string arithmetic as the hot path for decimal operations, while brick/math pays for an extra object-wrapping layer on top of GMP. Integer-only operations (mod, pow, powMod, base conversion) use GMP directly when the extension is available, combining the best of both backends. Realistic workflows like chained calculations or 100-iteration accumulations show a consistent 3-5x advantage, with immutability costing only a lightweight clone per operation (two properties: a string and an int).
MathMutable eliminates the clone overhead entirely for hot loops:
| Operation | MathMutable | Math (immutable) | brick/math |
|---|---|---|---|
| chained workflow | 1.964μs (±8.5%) | 2.294μs (±5.0%) | 14.149μs (±3.9%) |
| accumulate 100 | 38.086μs (±2.0%) | 41.339μs (±2.5%) | 147.063μs (±0.4%) |
| branch | 2.795μs (±4.0%) | 2.580μs (±3.3%) | 12.885μs (±20.7%) |
Run benchmarks yourself:
composer bench # ASCII table composer bench-md # Markdown table composer bench-md -- --group=integer # Filter by group
Compatibility
| PHP | Laravel |
|---|---|
| 8.1 | 10 |
| 8.2 | 10, 11, 12 |
| 8.3 | 10, 11, 12 |
| 8.4 | 10, 11, 12 |
Related
Math is also included in OpinHelpers, a collection of utilities for common PHP challenges.
Contributing
Contributions are welcome. Please open issues and submit pull requests.
# fix code style composer fix # run tests composer test # run tests with coverage composer cov # static analysis (src, level 9) composer stan # static analysis (tests, level 5) composer stan-tests
License
Math is open-source software licensed under the MIT license.
fab2s/math 适用场景与选型建议
fab2s/math 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 312.73k 次下载、GitHub Stars 达 3, 最近一次更新时间为 2019 年 07 月 31 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「decimal」 「math」 「Simple」 「laravel」 「bcmath」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 fab2s/math 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 fab2s/math 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 fab2s/math 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
LaTeX formula to PNG
Field for number with restricted count of digits and decimal places
PHP implementation of python's struct module.
Partition problem for balanced arrays splitting made easy.
Simple, chainable calculation library.
Easy to use arithmetic operations for decimals in PHP (using BCMath)
统计信息
- 总下载量: 312.73k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 3
- 点击次数: 31
- 依赖项目数: 2
- 推荐数: 1
其他信息
- 授权协议: MIT
- 更新时间: 2019-07-31