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。

我不介意存储价格较低的值,但是以无损的方式存储,例如,如果有一种数字类型将值存储为整数,但如果可以提高性能,则只能存储三位小数。

我认为对于此类错误,可能有更好的方法来计算 (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是底部具有 2 幂的最接近的分数,可以放入 32 位浮点数。

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

您应该始终始终使用小数进行财务计算,切勿浮动。

于 2012-04-28T15:29:56.563 回答
6

问题是 0.005f实际上是0.004999999888241291046142578125... 所以小于 0.005。这是最接近float0.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 回答