Laravel BCMath Cast: Integrating Precise Calculations into Eloquent Models
In my previous article, I demonstrated how to use PHP 8.4's BCMath Object API to solve floating-point precision issues and implemented a custom cast to integrate BCMath\Number.
However, every time I started a new project, I found myself copying and pasting the same cast code. This not only resulted in repetitive work but also increased maintenance costs.
Therefore, I packaged this cast into a small Laravel package: laravel-bcmath-cast.
Requirements
- PHP 8.4+ (required for BCMath\Number class)
- Laravel 11+
Installation
composer require yuwuhsien/laravel-bcmath-castUsage
Add the AsDecimal cast to your Eloquent model:
use YuWuHsien\Decimal\Casts\AsDecimal;
class Product extends Model
{
protected function casts(): array
{
return [
'price' => AsDecimal::class,
'cost' => AsDecimal::class,
'tax_rate' => AsDecimal::class,
];
}
}These attributes will be automatically converted to BCMath\Number objects, allowing you to perform precise calculations directly using operators:
$product = Product::find(1);
// Attributes are now BCMath\Number instances
$product->price instanceof Number; // true
// Perform precise calculations directly with operators
$margin = $product->price - $product->cost;
$marginPercent = ($margin / $product->cost) * new Number('100');
// Calculate total with tax
$total = $product->price * (new Number('1') + $product->tax_rate);Multiple Input Types Supported
use BcMath\Number;
// BCMath\Number object
$product->price = new Number('29.99');
// Numeric string (recommended for precision)
$product->price = '29.99';
// Integer
$product->price = 30;
// Float (works but triggers warning)
$product->price = 29.99;
// Warning: Float values may lose precision when converted to BCMath\Number.
// Use strings for exact decimal values (e.g., '19.99' instead of 19.99).Why does using floats trigger a warning? Floats inherently have precision issues, which is exactly why we use BCMath:
// Float precision issue
var_dump(0.1 + 0.2); // float(0.30000000000000004)
// Even converting to BCMath\Number cannot recover the lost precision
$num = new Number(0.1 + 0.2);
var_dump($num->value); // "0.30000000000000004"Therefore, if you need precise calculations, always use strings:
// Wrong: using float
$product->price = 19.99;
// Correct: using string
$product->price = '19.99';Database Column Types
If your data requires precise calculations, database columns must use DECIMAL as the column type, not FLOAT:
Schema::create('products', function (Blueprint $table) {
$table->decimal('price', 10, 2); // Correct: precise decimal storage
$table->decimal('cost', 10, 4); // Correct: can specify different precision
$table->float('amount'); // Wrong: loses precision
});For more related explanations, please refer to my previous article.
Final Thoughts
This package originated from the implementation experience I shared in my previous article. The purpose of making it a standalone package is simple:
To allow projects requiring precise calculations to install and use it via Composer, without having to copy and paste code every time.
If you're also developing applications that require precise numerical calculations (finance, accounting, e-commerce, inventory management, etc.), try this package in your projects:
composer require yuwuhsien/laravel-bcmath-cast