1. 前方にピットがあります
足し算、引き算、掛け算、割り算などの演算子を使用して浮動小数点数を計算すると、特に財務データの計算では予期せぬ結果が出ることが多く、多くのエンジニアを悩ませてきました。たとえば、今日ようやく職場に到着したケース:
$a = 2586;
$b = 2585.98;
var_dump($a-$b);
望ましい結果は次のとおりです: float(0.02)
実績:
float(0.019999999999982)
人生には落とし穴がある、どこにでも気をつけて
2.アンチピットレイダー:
1. 100 を掛けて整数の加減算に変換し、100 で割って元に戻す...
2. number_format を使用して文字列に変換し、(float) を使用して強制的に戻します...
3. PHP は、実際にこの浮動小数点計算の問題を解決するために生まれた高精度計算関数ライブラリを提供します。
主な機能は次のとおりです。
bcadd — 2 つの高精度数を加算する
bccomp — 2 つの高精度数値を比較し、-1、0、1 を返します
bcdiv — 2 つの高精度数を除算する
bcmod — 高精度の数値剰余を見つける
bcmul — 2 つの高精度数を掛ける
bcpow — 高精度で数値を 2 乗する
bcpowmod — 数論で非常に一般的に使用される、高精度のデジタルべき乗と剰余を求める
bcscale — デフォルトの小数点以下の桁数を設定します。これは、Linux bc の「scale=」に相当します。
bcsqrt — 高精度数の平方根を求める
bcsub — 2 つの高精度数を減算する
最初の 2 つの不正な方法はテストされません. bcsub を使用して、2 つの数値の減算の 3 番目の例をテストします.
bcsubの使い方を初見(公式サイトより)
文字列 bcsub (文字列 $left_operand 、文字列 $right_operand [, int $scale = int ] )
パラメータ
left_operand 文字列型の左オペランド。
right_operand 文字列型の右オペランド。
scale このオプションのパラメーターは、結果の小数点以下の桁数を設定するために使用されます。bcscale() を使用して、すべての関数のグローバルなデフォルト スケールを設定することもできます。
戻り値は、減算の結果を文字列型で返します。
テストコード:
var_dump(bcsub($a,$b,2));
結果
0.02
その他の機能については、PHP公式サイトを参照してください
3. ピットがある理由:
PHP のバグ? いいえ、これは基本的にすべての言語が遭遇する問題なので、基本的にほとんどの言語は正確な計算のためにクラス ライブラリまたは関数ライブラリを提供します。
この理由を理解するには、まず浮動小数点数 (IEEE 754) の表現を知る必要があります。
例として 64 ビット長 (倍精度) の浮動小数点数は、1 符号ビット (E)、11 指数ビット (Q)、および 52 ビット仮数 (M) を使用して (合計 64 ビット)。
符号ビット: 最上位ビットはデータの正負を示し、0 は正数を示し、1 は負数を示します。
指数ビット: 2 を底とするデータのべき乗を示し、指数はオフセット コードで表されます。
仮数部:データの小数点以下の有効桁数を示します。
ここで重要な点は、2 進数での 10 進数の表現、10 進数を 2 進数に変換する方法です。
アルゴリズムは、小数がなくなるまで 2 を掛けることです。以下は例です。0.9 は 2 進数で表されます。
0.9*2=1.8 整数部 1 を取得
0.8 (1.8 の小数部分)*2=1.6 は整数部分 1 を取ります
0.6*2=1.2 整数部分 1 を取得
0.2*2=0.4 整数部 0 を取る
0.4*2=0.8 整数部 0 を取る
0.8*2=1.6 整数部分 1 を取る
0.6*2=1.2 整数部 0 を取得
.........
0.9 のバイナリ表現は (上から下へ): 1100100100100......
注: 上記の計算プロセスは循環的です。つまり、*2 は小数部分を削除できないため、アルゴリズムは無限に続きます。明らかに、10 進数の 2 進数表現は、正確であることが不可能な場合があります。実はその理由は非常に単純で、10 進法で 1/3 を正確に表すことができるのでしょうか。同様に、バイナリ システムは 1/10 を正確に表すことができません。これは、浮動小数点減算に「無尽蔵の」精度損失の問題がある理由も説明しています。
言い換えれば、コンピューターに正確な数として保存されておらず、正確ではない小数が表示されます。したがって、数を加算、減算、乗算、および除算すると、予期しない結果が表示されます。
4.ピット防止のヒント
上記の理由から、浮動小数点数の結果が最後の桁まで正確であるとは決して信じず、2 つの浮動小数点数が等しいかどうかを決して比較しないでください。本当により高い精度が必要な場合は、任意精度の数学関数または gmp 関数を使用する必要があります