Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPで任意精度演算を行って「正しい」金額計算をする方法 / Perform arbitrar...

PHPで任意精度演算を行って「正しい」金額計算をする方法 / Perform arbitrary precision arithmetic in PHP to achieve "accurate" monetary calculations

YAMAOKA Hiroyuki

March 24, 2023
Tweet

More Decks by YAMAOKA Hiroyuki

Other Decks in Programming

Transcript

  1. 0.1 + 0.2 ≠ 0.3 php > var_dump(0.1 + 0.2);

    float(0.30000000000000004) 🤔
  2. fl oat / double - PHPで、整数は int(integer)という型 - 10進数、16進数、8進数、2進数で指定可能 -

    「整数以外」を表現するのに浮動小数点数を使う - fl oat(ただし倍精度)、double - intの上限(PHP_INT_MAX)を超える整数も対象
  3. 2進数の世界 - コンピューターは2進数で動いている - 0 or 1(OFF or ON)のバイナリの世界 -

    10進数の小数は2進数の世界で表現可能か? - 0.1(2進数)は2-1(10進数の0.5) 
 0.01(2進数)は2-2(10進数の0.25)
  4. 浮動小数点数 - 10進数の実数を有限桁の2進数の近似値で表現 - 浮動小数点( fl oating-point) - -1 x

    0.101010101010101 x 2-1 と 
 -1 x 1.01010101010101 x 2-2 は同じ 
 符号、仮数、指数 - 複数フォーマットがある(そのうちの1つがIEEE 754)
  5. BC Math 関数 - GNU bc(Basic Calculator)からのfork - https://www.gnu.org/software/bc/ -

    PHPを「--enable-bcmath」を付けて構築 - 関数: bcadd、bccomp、bcdiv、bcmod、bcmul、 bcpow、bcpowmod、bcscale、bcsqrt、bcsub
  6. 例えば足し算 php > var_dump(bcadd('0.1', '0.2', 1)); string(3) "0.3" php >

    var_dump(bcadd('0.1', '0.2', 20)); string(22) "0.30000000000000000000"
  7. GMP 関数 - GNU Multiple Precision Arithmetic Library - https://gmplib.org/

    - PHPを「--with-gmp」を付けて構築 - とてもたくさんの数学関数群
  8. 雑なベンチマーク <?php $floatStart = microtime(true); for ($i = 0; $i

    < 10000000; $i++) { $result = 0.1 + 0.2; } $floatEnd = microtime(true); $bcStart = microtime(true); for ($i = 0; $i < 10000000; $i++) { $result = bcadd('0.1', '0.2', 1); } $bcEnd = microtime(true); echo 'float: ' . ($floatEnd - $floatStart) . "ms\n"; echo 'bcadd: ' . ($bcEnd - $bcStart) . "ms\n";
  9. 注意: 引数は文字列で - BC Math関数に渡す引数は「文字列」で - fl oat表記で渡すとstringにキャストされる - 例:

    0.00001 は 1.0E-5 になってしまう - 数値を表す文字列ではないのでエラーになる可能性
  10. ただし - BC Math関数に用意されているのは 
 基本的な算術関数のみであることに注意 - 例えば、 fl oorやceil、roundのような関数はない

    - 雑に fl oor関数等に渡すと fl oatに変換されるので 
 計算誤差が発生する可能性がある