1

プログラムが意図したとおりに動作しない理由をテストする際に、失敗していると思われる計算を即時ウィンドウに入力してみました。

Math.Floor(1.0f)
1.0 - correct

でも:

200f * 0.005f
1.0

Math.Floor(200f * 0.005f)
0.0 - incorrect

さらに:

(float)(200f * 0.005f)
1.0

Math.Floor((float)(200f * 0.005f))
0.0 - incorrect

おそらく、いくつかのフロート損失が発生しています。たとえば、0.99963 ≠ 1.00127 です。

より安価な値を保存してもかまいませんが、非損失の方法で、たとえば、整数と同じように値を保存する数値型があった場合、パフォーマンスを向上させることができれば、小数点以下 3 桁までです。

このようなエラーに関しては、おそらく (n * 0.005f) を計算するより良い方法があると思います。

編集:

TY、ソリューション:

Math.Floor(200m * 0.005m)

また、私が理解しているように、1/200 を 1/256 に変更してもかまわない場合、これは機能します。

Math.Floor(200f * 0.00390625f)

私が使用しているソリューション。これは私のプログラムで取得できる最も近いものであり、問​​題なく動作するようです:

float x = ...;
UInt16 n = 200;
decimal d = 1m / n;
... = Math.Floor((decimal)x * d)
4

3 に答える 3

13

浮動小数点数は、分母が 2 の累乗の分数として数値を表します。つまり、1/2、3/4、または 19/256 を正確に表すことができます。.005 は 1/200 であり、200 は 2 の累乗ではないため、代わり0.005fに、32 ビット浮動小数点数に収まる底部に 2 の累乗がある最も近い分数が得られます。

小数は、分母が 10 の累乗の分数として数値を表します。float と同様に、そのパターンに適合しない数値を表現しようとすると、エラーが発生します。1m/333mたとえば、 は、分母が 10 の累乗で、有効桁数が 29 桁以下の 1/333 に最も近い数を返します。0.005 は 5/1000 であり、これは 10 のべき乗である0.005mため、正確な表現が得られます。あなたが支払う代償は、小数が浮動小数点数よりもはるかに大きくて遅いということです。

財務計算には常に小数を使用する必要があり、浮動小数点数は使用しないでください。

于 2012-04-28T15:29:56.563 に答える
6

問題は、0.005f が実際には0.004999999888241291046142578125 であり、0.005 未満であることです。floatこれが 0.005に最も近い値です。これに 200 を掛けると、1 未満になります。

代わりに -から変換せずdecimalに常に -を使用する場合、この特定のシナリオでは問題ないはずです。そう:float

decimal x = 0.005m;
decimal y = 200m;
decimal z = x * y;
Console.WriteLine(z == 1m); // True

decimalただし、この手段に「無限の精度」があると想定しないでください。これは依然として精度が制限された浮動小数点型です。これは単なる浮動小数点型であるため、0.005 は正確に表現できます。

于 2012-04-28T15:31:12.480 に答える
2

浮動小数点の精度の問題を許容できない場合は、 を使用してdecimalください。

http://msdn.microsoft.com/en-us/library/364x0z75.aspx

最終的decimalには、精度の問題もあります (28 ~ 29 桁の有効数字が許容されます)。サポートされている範囲 ((-7.9 x 10^28 から 7.9 x 10^28) / (100^28)) で作業している場合、それらの影響を受ける可能性はほとんどありません。

于 2012-04-28T15:19:36.007 に答える