Laravel BCMath Cast:將精確計算整合進 Eloquent 模型
在 上一篇 文章中,展示了如何使用 PHP 8.4 的 BCMath Object API 解決浮點數精度問題,並實作了一個自定義 cast 來整合 BCMath\Number。
然而,每當建立新專案時,我發現自己總是在複製貼上相同的 cast 程式碼。這不僅重複勞動,也增加了維護成本 — 當需要修正錯誤或改進功能時,必須在多個專案中同步更新。 因此,我將這個 cast 封裝成一個小巧的 Laravel package:laravel-bcmath-cast。
環境需求
- PHP 8.4+(BCMath\Number 類別的必要條件)
- Laravel 11+
安裝
shell
composer require yuwuhsien/laravel-bcmath-cast使用
在 Eloquent 模型中加入 AsDecimal cast:
php
use YuWuHsien\Decimal\Casts\AsDecimal;
class Product extends Model
{
protected function casts(): array
{
return [
'price' => AsDecimal::class,
'cost' => AsDecimal::class,
'tax_rate' => AsDecimal::class,
];
}
}這些屬性會自動轉換為 BCMath\Number 物件,可以直接使用運算子進行精確計算:
php
$product = Product::find(1);
// 屬性已經是 BCMath\Number 實例
$product->price instanceof Number; // true
// 直接使用運算子進行精確計算
$margin = $product->price - $product->cost;
$marginPercent = ($margin / $product->cost) * new Number('100');
// 計算含稅總價
$total = $product->price * (new Number('1') + $product->tax_rate);支援多種輸入型別
php
use BcMath\Number;
// BCMath\Number 物件
$product->price = new Number('29.99');
// 數值字串(推薦,保持精度)
$product->price = '29.99';
// 整數
$product->price = 30;
// 浮點數(可用但會觸發警告)
$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).為什麼浮點數會觸發警告? 浮點數本身就存在精度問題,這正是使用 BCMath 的原因:
php
// 浮點數的精度問題
var_dump(0.1 + 0.2); // float(0.30000000000000004)
// 即使轉換成 BCMath\Number,也無法救回已經失去的精度
$num = new Number(0.1 + 0.2);
var_dump($num->value); // "0.30000000000000004"因此,如果需要精確的計算,需要始終使用字串:
php
// 錯誤:使用浮點數
$product->price = 19.99;
// 正確:使用字串
$product->price = '19.99';資料庫欄位類型
如果資料需要精確計算,資料庫欄位必須使用 DECIMAL 作為欄位類型,而非 FLOAT:
php
Schema::create('products', function (Blueprint $table) {
$table->decimal('price', 10, 2); // 正確:精確的小數儲存
$table->decimal('cost', 10, 4); // 正確:可指定不同精度
$table->float('amount'); // 錯誤:會失去精度
});更多相關的說明,可以參考 上一篇 文章。
最後
這個 package 源自我在 上一篇 文章中分享的實作經驗。將它獨立出來的目的很簡單:
讓需要精確計算的專案可以透過 Composer 安裝使用,而不需要每次都複製貼上程式碼。
如果你也在開發需要精確數值計算的應用(金融、會計、電商、庫存管理等),可以在你的專案中試試這個 package:
shell
composer require yuwuhsien/laravel-bcmath-cast