Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Bignumbers Laravel Package

litipk/php-bignumbers

Arbitrary-precision arithmetic for PHP with immutable BigInteger and BigDecimal classes. Perform exact integer and decimal math, comparisons, rounding, and conversions without floating-point precision loss—ideal for money, scientific, and large-number calculations.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and smoke-test before anything else. The package is archived (last release 0.8.6, 2017), so the first ten minutes are about proving it still runs on your runtime, not about learning the API.

composer require litipk/php-bignumbers:0.8.6
php -r '
require "vendor/autoload.php";
use Litipk\BigNumbers\Decimal;
$a = Decimal::fromString("0.1");
$b = Decimal::fromString("0.2");
echo $a->add($b)->asString(), PHP_EOL;   // must be exactly 0.3
'

If that prints 0.3 you have a working backend. If it errors, you are missing ext-gmp/ext-bcmath (it will fall back to a pure-PHP provider) or you are hitting a PHP 8 deprecation — both are covered in Gotchas.

Where to look first, in this order:

  1. src/ — the README is thin and the class names in the description (BigDecimal, BigRational) do not always match the actual FQCNs across versions. Confirm you are using Litipk\BigNumbers\Decimal (and whatever the rational type is named in 0.8.6) before writing anything.
  2. The Decimal public methods: fromString / fromInteger / fromFloat, add sub mul div mod pow, comp / equals / isGreaterThan, getScale / asString / asFloat.
  3. The backend-provider classes — you need to know which one your container, CI, and queue workers will actually load.

First use case: a single money path. Pick one calc that currently produces "off by a cent" tickets — invoice line totals with tax, a discount percentage applied to a subtotal, or a currency conversion. Do not try to convert the whole codebase. A representative before/after:

// before: float drift
$total = $price * $qty * (1 + $taxRate);

// after
use Litipk\BigNumbers\Decimal;

$total = Decimal::fromString($price)     // price arrives as a string, always
    ->mul(Decimal::fromInteger($line['qty']))
    ->mul(Decimal::fromInteger(1)->add(Decimal::fromString($taxRate)))
    ->asString();

If you cannot state the scale and rounding rule for that one calculation on paper, stop — the library will not save you from an undefined accounting policy.

Implementation Patterns

Wrap it behind an application-owned contract on day one. This is the single highest-value pattern, because it converts an abandonment risk into a one-class swap. No service provider or facade ships with the package, so you own the wiring.

namespace App\Support\Math;

use Litipk\BigNumbers\Decimal;

interface Calculator
{
    public function of(string $value): Decimal;          // returns the raw value object
    public function round(string $value, int $scale): string;
    public function sum(array $values, int $scale): string;
}

Keep the interface narrow and string-in/string-out at its edges. Domain code talks to Calculator; only the adapter uses Litipk. Write a second adapter over brick/math or bcmath behind the same interface later — that is how you get differential tests for free.

Set the default scale once, in a provider, from config. Precision is static global state; centralise it.

// config/bignumbers.php
return ['scale' => env('BIGNUMBERS_SCALE', 8)];

// AppServiceProvider::boot()
Decimal::setDefaultScale((int) config('bignumbers.scale'));

Never set it per-request or per-service — see Gotchas on Octane.

Use a custom Eloquent cast for persistence. There is no first-party cast, and this is where precision silently dies. Store as string/DECIMAL, never float/double.

class DecimalCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes): ?string
    {
        return $value === null ? null : (string) $value; // keep strings, hydrate lazily
    }

    public function set($model, $key, $value, $attributes): ?string
    {
        return $value === null ? null : Decimal::fromString((string) $value)->asString();
    }
}

Make the column's DECIMAL(p,s) precision wider than the computed scale, and add a serialization test asserting a round-trip through the database, a job payload, and json_encode all preserve the digits.

Compose a Money value object rather than passing Decimal around. Decimal has no currency, no scale policy, and no formatting rules — a Money does.

final class Money
{
    private function __construct(
        public readonly string $amount,
        public readonly string $currency,
    ) {}

    public static function of(string $amount, string $currency): self
    {
        return new self(Decimal::fromString($amount)->asString(), $currency);
    }

    public function add(self $other): self
    {
        // assert same currency
        return new self(
            Decimal::fromString($this->amount)->add(Decimal::fromString($other->amount))->asString(),
            $this->currency
        );
    }
}

Validate at the boundary, then trust strings inside. Add a rule for money input and a FormRequest normaliser so no float ever reaches the domain.

// app/Rules/DecimalString.php
public function passes($attribute, $value): bool
{
    return is_string($value) && preg_match('/^-?\d+(\.\d+)?$/', $value) === 1;
}

Format for humans in the API layer, not the domain. Do the rounding at the representation edge so the stored value keeps full precision.

// API Resource
'amount' => Decimal::fromString($this->amount)->asFloat(), // ❌ lossy
'amount' => $this->amount,                                   // ✅ raw string
'formatted' => number_format((float) $this->amount, 2),       // ✅ display only

Queue and JSON boundaries: serialize to string explicitly. Do not rely on object serialization surviving a queue payload or an event listener.

ProcessPayout::dispatch($money->amount, $money->currency);

Summing collections. Avoid a reduce that allocates a new immutable object per element in a hot loop; accumulate the raw strings and do one Decimal operation per chunk, or accept the cost knowingly (see Gotchas).

Gotchas and Tips

It is archived: treat every PHP upgrade as your bug. Last release was 2017-08-31 and there is no maintainer. Expect dynamic-property deprecations, implicit-nullable parameter signatures, and missing return types on PHP 8.2+. Pin the exact version ("litipk/php-bignumbers": "0.8.6"), add a PHPStan baseline for it, and assume you will vendor or fork it the first time a minor bump breaks it. For new money code, plan the adapter swap to brick/math.

Never construct from a float. Decimal::fromFloat(0.1) is already wrong before the library sees it — the binary representation has been baked in. Rule to enforce in review: grep for fromFloat and (float) in anything that touches storage or computation. Floats are acceptable at display/asFloat() time only.

Immutability means every operation returns a new value. Forgetting to assign is a silent no-op with no error:

$total->add($fee);        // ❌ discarded
$total = $total->add($fee); // ✅

This is the number-one day-to-day bug with the package. It is also what makes it safe to share instances across objects — lean on that in DTOs.

Backend divergence is a real failure mode. The library picks between GMP, BCMath, and a pure-PHP provider. A developer on GMP locally and a container without the extension in production can produce different limits or performance cliffs. Declare the extension in composer.json ("ext-bcmath": "*") and mirror it in your Dockerfile and CI matrix so every environment loads the same provider.

The pure-PHP fallback is orders of magnitude slower. Fine for one invoice; catastrophic in a reconciliation job or a per-line-item loop over 100k rows. Profile once with gmp present and once without, and know which one your queue workers get.

The default scale is process-global mutable state. Under Octane or a long-lived queue worker, a setDefaultScale() call in one request leaks into the next. Set it in boot() only; if you need per-context scale, pass scale explicitly to each operation instead of mutating the default.

Division and modulus need an explicit story. Check what your version does on divide-by-zero (throw vs. null vs. error) and what scale the quotient gets. Write a test for div with a repeating decimal — that is exactly where scale policy becomes visible.

Comparison across scales. equals on "1.0" and "1.00" should be true, but do not assume; assert it in a test and use comp() with an explicit scale if you need deterministic ordering for sorting or unique constraints.

Formatting: pad to scale yourself. Do not let asString() be your display format — it reflects internal precision, not presentation. Round and pad at the edge (round($scale), then format) and test the .5 behaviour against your accounting rules (half-up vs. banker's).

Serialization is not a given. An immutable value object with private state may json_encode into {} or an odd shape. Always convert to string before it crosses a queue, an event, a cache, or an API. Add a test that pushes a Money through serialize()/unserialize() and through a fake queue job.

Escape hatches worth knowing: MIT licence means you may vendor the source into app/Support/Math/Vendor/ and patch signatures yourself — that is the only defensible posture for anything critical. If you are evaluating this package rather than inheriting it, take the pattern (immutable value objects, explicit scale, string constructors) and implement it over brick/math or a thin bcmath wrapper; nothing in the design requires this specific dependency.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource