私の数値が間違って四捨五入されるのはなぜですか?
-
22-09-2019 - |
質問
これは、その場でのみ失敗する種類のコードのように感じますが、これを、私が見ているものを表すコード スニペットに適応させてみます。
float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);
コードをステップ実行すると、i==269、i2==268 になります。この違いを説明するには何が起こっているのでしょうか?
解決
浮動小数点演算は、宣伝されているよりも高い精度で実行できます。しかし、float f に格納するとすぐに、その余分な精度は失われます。もちろん、結果を int にキャストするまでは、2 番目のメソッドでも精度が失われることはありません。
編集:この質問を参照してください C# では、かっこで区切った場合とステートメントで区切った場合で浮動小数点の精度が異なるのはなぜですか? おそらく私が提供したよりも優れた説明が必要です。
他のヒント
浮動小数点精度を制限しており、バイナリではなく、小数に基づいています。ラウンドダウンよう小数点数13.45を正確、バイナリ浮動小数点で表現することができません。 20乗算は、さらに精度の損失を誇張します。この時点で、あなたは... 268.999を持って - ない269から268までの整数の切り捨てにそのための変換を
。最も近い整数に丸め取得するには、整数に背を変換する前に0.5を追加してみてください可能性があります。
は「完璧な」算術演算のために、あなたは、小数またはRational数値型を使用して試みることができる - 私はC#は両方のためのライブラリを持っていると信じていますが、確かではありませんよ。これらは、しかし、遅くなります。
編集 - 私は、それが利用可能であることについて間違っている可能性が - 私は、「小数」これまでに入力しますが、合理的ではないが見出されています。 10進浮動小数点だけのバイナリと同じように、不正確であるが、それはあまり驚くべき結果を与えるので、それは、私たちが使用している不正確さのようなものだ。
タグに置き換え
double f = myFloat * myConstInt;
とあなたが同じ答えを得るかどうかを確認します。
別の説明をしたいと思います。
注釈を付けたコードは次のとおりです (フロートを分析するためにメモリを調べました)。
float myFloat = 13.45; //In binary is 1101.01110011001100110011 int myConstInt = 20; float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal) int i = (int)f; // Turns float 269 into int 269 -- no surprises int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268
計算を詳しく見てみましょう。
f = 1101.01110011001100110011 * 10100 = 100001100.111111111111111 111
スペースの後の部分はビット 25 ~ 27 で、ビット 24 が切り上げられるため、全体の値は 269 に切り上げられます。
int i2 = (int)(myFloat * myConstInt)
myfloat は計算のために倍精度に拡張されます (0 が追加されます)。1101.0111001100110011001100000000000000000000000000000
myfloat * 20 = 100001100.11111111111111111100000000000000000000000000
ビット 54 以降は 0 であるため、丸めは行われません。キャストの結果は整数 268 になります。
(拡張精度が使用されている場合も、同様の説明が機能します。)
アップデート:私は答えを洗練させて、という本格的な記事を書きました。 フロートがフロートのように動作しない場合