C#和漂浮物的恶作剧

时间:2012-04-28 15:17:21

标签: c# floating-point rounding

在测试为什么我的程序没有按预期工作时,我尝试输入似乎未能进入即时窗口的计算。

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。

我不介意存储较少的pricise值,但是以非损耗的方式,例如,如果有一个数值类型将值存储为整数,但是只有三个小数位,如果它可以是高性能的。< / p>

我认为可能有更好的方法来计算(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)

3 个答案:

答案 0 :(得分:13)

Floats将数字表示为分母中幂为2的分数。也就是说,您可以准确地表示1/2,或3/4或19/256。因为.005是1/200,而200不是2的幂,相反,你得到的0.005f是最接近底部的幂为2的部分,可以适合32位浮点数。

小数将数字表示为分母中具有10的幂的分数。与浮点数一样,当您尝试表示不符合该模式的数字时,它们会引入错误。例如,1m/333m将为您提供最接近1/333的数字,其分数为10,有效数字为29或更少。由于0.005为5/1000,即10的幂,0.005m将为您提供精确的表示。您支付的价格是小数字比浮点数大得多且慢。

您应该始终始终使用小数进行财务计算,永远不会浮动。

答案 1 :(得分:6)

问题是0.005f 实际上 0.004999999888241291046142578125 ...所以小于0.005。这是与0.005最接近的float值。当你乘以200时,你得到的东西少于1。

如果您一直使用decimal - 而不是float转换 - 在此特定情况下 。所以:

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

但是,不要认为这意味着decimal具有无限精度&#34;。它仍然是一个精度有限的浮点类型 - 它只是一个浮点十进制点类型,所以0.005是完全可表示的。

答案 2 :(得分: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)),则不太可能受到它们的影响。