はじめに

PHP で moneyphp/money を使って浮動小数点数の誤差を考慮した金額を計算する方法。

TL;DR

  • composer require moneyphp/money でインストール
  • Money::JPY(3000) でインスタンス生成
  • multiply(1.1, Money::ROUND_UP) で丸めモードを指定して乗算
  • getAmount() で計算結果の取得
    • String なので必要に応じて (int) などでキャスト
  • 利用可能であれば 任意精度数学関数gmp 関数 を使っても OK

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 前置き
    2. セットアップ
    3. 使い方
  5. まとめ
  6. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 16.04.6 LTS"

$ php -v
PHP 7.3.16-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Mar 20 2020 13:51:21) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.16, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.16-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies

$ composer -V
Composer version 1.9.1 2019-11-01 17:20:17

$ composer info moneyphp/money
name : moneyphp/money
versions : * v3.3.1

詳細

前置き

PHP(Laravel)で「消費税込の金額を小数点以下は切り上げで計算」で ceil を使うと以下のように浮動小数点数の丸め誤差による金額のずれが発生する。

1
2
3
4
5
// Psy Shell(artisan tinker)上で計算
>>> 3000 * 1.1
// => 3300.0
>>> (int) ceil(3000 * 1.1)
// => 3301 ※期待値より1円高い

PHP: 浮動小数点数 にも記載されているが、高精度な処理が必要な場合は 任意精度数学関数gmp 関数 を使う必要がある。

gmp 関数を使うには gmp インストール手順 に記載されているように --with-gmp をつけて PHP のコンパイルが必要。

PHP のコンパイルまではやりたくなかったので、今回この方法は見送り。

※任意精度数学関数(今回で言うと bcmulbcdiv)は PHP のコンパイル無しで利用可能だが作業時に見落としていた。

他のやり方を探して moneyphp/money を見つけたので実際に試してみた。

セットアップ

composer require moneyphp/money でインストール。

1
2
3
4
$ composer require moneyphp/money
Using version ^3.3 for moneyphp/money
...
Package manifest generated successfully.

使い方

参考: Money for PHP — Money 3.0.0 documentation

※「消費税込の金額を小数点以下は切り上げで計算」に関連するもののみ記載。その他は↑のドキュメント参照。

Money::JPY(3000) でインスタンスの作成。

1
2
3
use Money\Money;

$jpy = Money::JPY(3000);

multiply で乗算。

1
$jpy_with_tax = $jpy->multiply(1.1);

multiply の第2引数で丸めモードの指定が可能、デフォルトは ROUND_HALF_UP

1
2
3
4
5
6
7
8
9
$jpy_with_tax = $jpy->multiply(1.1, Money::ROUND_UP);
// Money::ROUND_HALF_DOWN
// Money::ROUND_HALF_EVEN
// Money::ROUND_HALF_ODD
// Money::ROUND_HALF_UP ***** default *****
// Money::ROUND_UP
// Money::ROUND_DOWN
// Money::ROUND_HALF_POSITIVE_INFINITY
// Money::ROUND_HALF_NEGATIVE_INFINITY

getAmount で値の取得。String で返却されるので、数値型として受け取りたい場合は (int) でキャストする。

1
2
3
4
5
$jpy_with_tax->getAmount();
// => "3300"

(int) $jpy_with_tax->getAmount();
// => 3300

まとめ

  • composer require moneyphp/money でインストール
  • Money::JPY(3000) でインスタンス生成
  • multiply(1.1, Money::ROUND_UP) で丸めモードを指定して乗算
  • getAmount() で計算結果の取得
    • String なので必要に応じて (int) などでキャスト
  • 利用可能であれば 任意精度数学関数gmp 関数 を使っても OK

参考文献

関連記事