在测试为什么我的程序没有按预期工作时,我尝试输入似乎未能进入即时窗口的计算。
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)
答案 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)),则不太可能受到它们的影响。