2

次の式が使用される C++ アプリ (Windows 7、64 ビット、VS 2008) を開発します (すべての変数は double 型です)。

mValue = floor(mValue/mStepping)*mStepping;

アイデアは、数値を指定された小数点以下の桁数に短縮することです。これを行うためのより良い方法があると思いますが、これはここでの質問ではありません (しかし、より良いオプションがある場合は、それを取り入れてください!)。

mValue はユーザー入力から取得されるため、ほとんどの場合、小数点以下の桁数は問題ありません。ただし、出力が入力と異なる場合もあります。

たとえば、mStepping の値は 0.1 です (小数点以下 1 桁に丸める必要があります)。mValue の値が 14.6 であれば、すべて問題ありません。mValue が 14.7 の場合、結果は 14.6 です。

では、なぜ floor(14.7/0.1)*0.1 = 14.6 なのですか?

他の値をテストしたところ、それらの約 20% で 0.1 の差がありました。さらに調べてみると、14.7/0.1 には 147.0 とは異なるバイナリ エンコーディングがあることがわかりました。

14.7/0.1 = ff ff ff ff ff 5f 62 40
147.0    = 00 00 00 00 00 60 62 40

同じ数値が double として別の方法でエンコードされる可能性があることを理解しています。しかし、なぜ floor() はそれらを異なる方法で処理するのでしょうか? そして、私はそれに対して何ができますか?

4

4 に答える 4

6

よくある問題: すべての正確な小数を 2 進数で正確に表現できるわけではありません。あなたの場合、それ0.1もそう14.7です。0.1 で割ると、実際には 147 に到達するのではなく、それよりわずかに小さい値になります。

14.699999999999999289457264239899814128875732421875

0.1 を掛けると、次のようになります。

0.1000000000000000055511151231257827021181583404541

到着地:

146.999999999999971578290569595992565155029296875

問題が見え始めていると思いますよね?その数をフローリングすると、明らかに 146 になります。

あなたはそれに対して何ができますか?正確な 10 進数の結果が必要な場合は、小数を表す数値型または bignum ライブラリを使用します。

ああ、補足: いいえ、同じ数に異なる表現はありません。double数値とは何か、その動作の解釈が浮動小数点演算とは異なるだけです。

于 2013-05-08T12:52:15.577 に答える
3

10 進数と小数の浮動小数点表現の不正確さに関する他の回答に加えて、floor() は、浮動小数点数の有効桁数に対する 10 進数表現を計算するときにも間違った選択です。代わりに丸めを行う必要があります-結果は浮動小数点数ではなく、他の方法で保存する必要があります。

于 2013-05-08T12:58:29.850 に答える