1

奇妙な問題があります:

$testTotal = 0;
foreach($completeBankArray as $bank){
   var_dump($testTotal);
   echo  " + ";
   var_dump(floatval($bank["amount"]));
   echo " = ".(floatval($testTotal) + floatval($bank["amount"]))."</br>";
   $testTotal = floatval(floatval($testTotal) + floatval($bank["amount"]));

そして、これは私が得る出力です:

------------------//--------------------
float(282486.09) + float(15) = 282501.09
float(282501.09) + float(3.49) = 282504.58
float(282504.58) + float(22.98) = 282527.55999999
float(282527.55999999) + float(5.2) = 282532.76
float(282532.76) + float(39.98) = 282572.73999999
float(282572.73999999) + float(2.6) = 282575.33999999
float(282575.33999999) + float(2.99) = 282578.32999999 
------------------//----------------------- 

これはどのように可能ですか、私は何をしているのですか?

4

4 に答える 4

7

あなたは何も悪いことをしていません。フロートは不正確なことで有名です。ドキュメントから(巨大な赤い警告ボックス内):

浮動小数点数の精度には制限があります。システムによって異なりますが、PHPは通常IEEE 754倍精度形式を使用します。これにより、1.11e-16のオーダーの丸めにより、最大の相対誤差が発生します。非初等算術演算ではエラーが大きくなる可能性があります。もちろん、複数の演算が複合されている場合は、エラーの伝播を考慮する必要があります。

さらに、0.1や0.7のように、基数10の浮動小数点数として正確に表現できる有理数は、仮数のサイズに関係なく、内部で使用される基数2の浮動小数点数として正確に表現されません。したがって、精度を少し損なうことなく、対応する内部バイナリに変換することはできません。これにより、混乱を招く結果が生じる可能性があります。たとえば、floor((0.1 + 0.7)* 10)は、内部表現が7.9999999999999991118 ...のようになるため、通常、期待される8ではなく7を返します。

したがって、浮動小数点数の結果を最後の桁まで信頼しないでください。また、浮動小数点数を直接比較して等しいかどうかを確認しないでください。より高い精度が必要な場合は、任意精度の数学関数gmp関数を使用できます。

于 2012-09-05T23:20:31.743 に答える
1

フロートは決して正確ではなく、長期的にはかなり異なります。精密な数学を使用している場合は、bc ライブラリについてお読みください。

于 2012-09-05T23:20:45.180 に答える
0

古典的な数値精度の例。コンピューターは浮動小数点数を2進数で格納します。

簡単に言うと、コンピューターは一部の浮動小数点数を2進数で正確に表すことができないということです。

長い答えは、数値ベース間を移動することです。フロートがある場合、分母に含まれるものを2の累乗の小数に分解できない限り、2進数で完全に表すことはできません。

于 2012-09-05T23:21:38.773 に答える
0

他の回答は、フロートでこの動作が発生する理由についての洞察を与えてくれました。

お金を扱っている場合、問題の解決策の 1 つは、浮動小数点数の代わりに整数を使用し、ドルの代わりにセントを扱うことです。あとは、小数点を含むように出力をフォーマットするだけです。

于 2012-09-05T23:57:09.343 に答える