24151.40 - 31891.10 = -7739.699999999997
これらの 2 つの数値を、タイプが decimal(14,2) 24151.40 31891.10 の MySQL テーブルから取得します。上記のとおりに保存され、PHP とまったく同じようにエコーされます。しかし、最初の値から 2 番目の値を引くと、-7,739.7 ではなく、-7739.699999999997 という数値が得られます。なぜ余分な精度? そして、それはどこから来ているのですか?
1 足す 1 は 2 ですよね?.2 たす 1.4 かける 10 はどうですか? それは16に等しいですよね?PHP (または他のほとんどのプログラミング言語) で計算を行っている場合は、そうではありません。
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
これは、浮動小数点数が内部でどのように処理されるかによるものです。これらは固定の小数点以下の桁数で表され、期待どおりに合計されない数値になる可能性があります。内部的には、.2 プラス 1.4 かける 10 の例は、およそ 15.9999999998 程度に計算されます。この種の計算は、パーセンテージのように正確である必要のない数値を扱う場合に適しています。しかし、お金を扱う場合、どこかで不足しているペニーまたはドルがすぐに追加され、不足しているお金が不足していることを好む人はいないため、精度が重要です。
BC数学ソリューション
幸いなことに、PHP はBC Math 拡張機能を提供します。これは、「任意精度の数学のために、PHP は、文字列として表される任意のサイズと精度の数値をサポートする Binary Calculator を提供します」。つまり、この拡張機能を使用して、金額を正確に計算できます。BC Math 拡張機能には、足し算、引き算、掛け算、割り算など、最も一般的な精度の演算を実行できる関数が含まれています。
より良い例
上記と同じ例ですが、bcadd() 関数を使用して計算を行っています。3 つのパラメーターが必要です。最初の 2 つは追加したい値で、3 番目は正確にしたい小数点以下の桁数です。お金を扱っているので、精度を小数点以下 2 桁に設定します。
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.
PHP には、MySQL のような 10 進数型はありません。浮動小数点数を使用します。フロートは不正確であることで有名です。
これを解決するには、以下を調べてくださいnumber_format
。
echo number_format(24151.40 - 31891.10, 2, '.', '');
より正確な数値操作については、PHP の数学拡張機能を参照することもできます。
これは、科学的に 1.FRACTAL * 2^exponential power に関連する一般的な float/double 精度レートに関係しています。接頭辞 1 があるため、技術的にゼロなどというものはなく、取得できる 0 に最も近い値は 1.0 * 2 ^ -127 で、.000000[127 0s]00001 です。
回答を特定の精度で四捨五入することにより、丸め係数により、より正確な回答が得られます。
http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_round