本記事の環境
ホストOS:Windows 10
ゲストOS:CentOS 7
VirtualBox + Vagrant
Tera Term
remiリポジトリ
php7.3
前段:PHPの数値計算系メソッドとは?
PHPには四捨五入、切り上げ、切り下げのメソッドが存在します。
※四捨五入:round、切り上げ:ceil、切り下げ:floor
要件1:数値の丸め方について
- 「四捨五入」「切り上げ」「切り下げ」と「切り捨て」がしたい。
- 有効桁数を指定したい。
問題
- 他言語にある「切り捨て」がPHPにはなかった。
- roundは、有効桁数を指定できるが、ceilとfloorはできない。
※round([対象値], [有効桁数]);
有効桁数:小数第一位が0、小数部はそこからインクリメント、
整数部はデクリメント。
※ceil([対象値]);
※floor([対象値]);
対処方法
- 「切り捨て」メソッドを作る。
- 有効桁数を指定できる「切り上げ:ceil」「切り下げ:floor」メソッドを作る。
※実装方法はネット上に複数パターン存在します。
※例1:補正値を加算してから処理を実施し、後で補正値を取り除く。
※例2:桁数を落として小数部を取り除いてから桁数を戻す。
要件2:計算結果について
- 正しい計算結果を出したい。
問題
- 2進数で表せない値を与えると結果に丸め誤差が生じる。(他言語も同様)
floor((0.1 + 0.7) * 10); // 結果:7 (7.999999999999…に対して切り下げ処理を実施。) floor((0.1 + 0.7 + 0.5) * 10); // 結果:12 (12.999999999999…に対して切り下げ処理を実施。)
対処方法
- BCMathライブラリ(任意精度数学関数)を使うことにした。
yum install --enablerepo=remi-php73 php-bcmath systemctl restart httpd
その他注意点
- -0.5よりも大きく、0未満の負の数についてroundを使用すると「-0」へ変換される。
round(-0.49999999999999, 0); // 結果:-0 (絶対値の0.49999999999999を四捨五入して、その結果にマイナスをつけるため。)
- 丸め誤差が生じるため、float(浮動小数点数値)同士の比較はやめた方がいい。文字列型にキャストして比較するか、bccomp()を使用するか、gmp()を使用するか。
まとめ
PHPで数値計算を行う場合は、十分な仕様理解と(自作する場合は)作業工数が必要。
参考
https://gotohayato.com/content/491
https://www.sejuku.net/blog/25405
https://www.sejuku.net/blog/48961
https://teratail.com/questions/11119
http://shiru-web.com/2018/04/14/01-81/
https://web-dev.xyz/php-round/