web-dev-qa-db-ja.com

PHP浮動小数点計算2小数点

数学の計算の問題を得た。

_$a = 34.56

$b = 34.55
_

_$a_この図を取得するためにいくつかの計算を行います

_$b_は、この数値を取得するために最も近い0.05に丸めています

何が起こるか

_$c = $b - $a
_

おそらく-0.01ですが、_$c_をエコー出力すると、-0.00988888888888と表示されます

number_format($c, 2)を使用しようとしていますが、出力は0.00です。

_$a_および_$b_が小数点以下2桁で、後ろに隠し番号がないことを確認するにはどうすればよいですか。

私のPHPの知識では、_number_format_は表示をフォーマットすることしかできませんが、値は実際には2桁ではありません。

私はここから助けを得ることができると思います。これは本当に私を苛立たせました。

25
Shiro

お試しください sprintf("%.2f", $c);

浮動小数点数は、2の累乗に基づくIEEE表記で表されるため、10進数の終了は2進数の終了ではない可能性があるため、後続の数字が表示されます。

可変長コーダーで提案されているように、必要な精度がわかっていて、変更されない場合(たとえば、お金を扱っている場合)、固定小数点数を使用する、つまり、ドルではなくセントで数値を表現する方がよい場合があります。

$a = 3456;

$b = 3455;

$c = $b - $a;

sprintf ("%.2f", $c/100.0);

これにより、印刷前に多くの計算を行っても、丸め誤差は発生しません。

53
Charles Ma

round() を使用:

_$c = round($b - $a, 2);
_

注:必要に応じて丸めモードを選択することもできます。

編集:Ok sprintf()が何をしているかわかりませんnumber_format()ではない:

_$c = number_format($b - $a, 2);
_

_$c = sprintf("%.2f", $b - $a);
_

15
cletus

Bcmathライブラリを使用するだけで、これらすべての問題をきれいに回避できます。

ドキュメントを読んで、引数を文字列として渡すか、数値データ型として渡すかに注意してください。

5
Robert L

ネイティブ計算:

$a = 34.56;
$b = 34.55;
$c = $b - $a; // -0.010000000000005    

期待どおりに機能します(!実数の計算には常にBC関数を使用します。問題はすべてのCベースのプラットフォームで発生します):

$a = '34.56';
$b = '34.55';
$c = bcsub($b, $a, 4); // -0.0100    
4

Floatを使用して計算を行っているときに、この問題にも最近遭遇しました。たとえば、2つの浮動小数点数があり、減算してフォーマットすると、値は-0.00でした。

$floatOne = 267.58;
$floatTwo = 267.58;
$result = number_format($floatOne - floatTwo, 2);
print $result; //printed a string -0.00

私がしたことは:

$result = abs($floatOne - $floatTwo);// Made the number positive
print money_format('%i', $result); // printed the desired precision 0.00

私の解決策では、floatOneがfloatTwo未満になることはありません。 money_format 関数は、システムにstrfmon機能がある場合にのみ定義され、Windowsにはありません。

4
jtaz

浮動小数点数のトラップの1つに遭遇しました。彼らは常に正確な小数を表すことができないことに注意してください。正確な10進数値が必要な場合は、整数を使用し、最後に必要な精度で除算することをお勧めします。

たとえば、ドル(または好みの通貨)を再設定する浮動小数点数で計算を行っている場合、実際には整数セントで計算する必要があります。

それでも、浮動小数点数の減算によりエラーまたは奇妙な値が発生するという同様の問題で誰かがこのページにアクセスした場合。この問題についてもう少し詳しく説明したいと思います。原因は浮動小数点数です。また、オペレーティングシステムやプログラミング言語のバージョンによって、動作が異なる場合があります。

問題を説明するために、以下の簡単な例で理由を説明します。

PHP=に直接関係するものではなく、バグでもありません。ただし、すべてのプログラマーがこの問題に注意する必要があります。

この問題は、20年前にも多くの命を奪いました。

1991年2月25日、MIM-104 Patriotミサイルバッテリーの浮動小数点計算におけるこの問題により、サウジアラビアのダーランに到着するスカッドミサイルを迎撃できなくなり、米陸軍の第14クォーターマスター分遣隊から28人の兵士が死亡しました。

しかし、なぜそれが起こるのですか?

その理由は、浮動小数点値は限られた精度を表すためです。そのため、値は処理後に同じ文字列表現を持たない場合があります。また、スクリプトに浮動小数点値を書き込んで、数値演算を行わずに直接出力することも含まれます。

単純な例:

$a = '36';
$b = '-35.99';
echo ($a + $b);

あなたはそれが0.01を印刷することを期待するでしょう?しかし、それは0.009999999999998のような非常に奇妙な答えを出力します

他の数値と同様に、浮動小数点数doubleまたはfloatは、0と1の文字列としてメモリに格納されます。浮動小数点が整数とどのように異なるかは、0と1をどのように解釈するかという点にあります。保管方法には多くの標準があります。

浮動小数点数は通常、左から右に、符号ビット、指数フィールド、および仮数または仮数としてコンピューターデータにパックされます。

十分なスペースがないため、10進数は2進数では適切に表現されません。だから、uouは0.3333333の1/3を正確に表現できません... 2進数の浮動小数点数として0.01を表すことができないのは、同じ理由によるものです。 1/100は0.00000010100011110101110000 ..... 10100011110101110000を繰り返します。

0.01がバイナリで01000111101011100001010の簡略化されたシステムトランケート形式で保持されている場合、10進数に変換すると、0.0099999のように読み取られます....システムによっては(64ビットコンピューターは32ビットよりはるかに精度が高くなります) )。この場合、オペレーティングシステムは、見た目どおりに印刷するか、より人間が読みやすい方法で作成するかを決定します。したがって、それをどのように表現するかはマシンに依存します。ただし、さまざまな方法で言語レベルで保護できます。

結果をフォーマットする場合、echo number_format(0.009999999999998、2); 0.01を出力します。

これは、この場合、読み取り方法と必要な精度を指示するためです。

2
Selay