.NET上的双精度问题

我有一个简单的C#函数:

public static double Floor(double value, double step) { return Math.Floor(value / step) * step; } 

计算更高的数字,低于或等于“值”,即“步数”的倍数。 但它缺乏精确性,如下面的测试所示:

 [TestMethod()] public void FloorTest() { int decimals = 6; double value = 5F; double step = 2F; double expected = 4F; double actual = Class.Floor(value, step); Assert.AreEqual(expected, actual); value = -11.5F; step = 1.1F; expected = -12.1F; actual = Class.Floor(value, step); Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals)); Assert.AreEqual(expected, actual); } 

第一个和第二个断言都可以,但第三个断言失败,因为结果只等到第6个小数位。 这是为什么? 有没有办法纠正这个?

更新如果我调试测试,我看到值相等,直到第8个小数位而不是第6个,可能是因为Math.Round引入了一些不精确。

注意在我的测试代码中,我写了“F”后缀(显式浮点常量),其中我的意思是“D”(双),所以如果我改变它,我可以有更多的精度。

如果省略所有F后缀(即-12.1而不是-12.1F ),您将获得更多相等的几位数。 由于F您的常量(尤其是预期值)现在是浮点数。 如果您是故意这样做,请解释。

但是对于其余的我同意比较双重或浮动值的平等的其他答案,它只是不可靠。

计算机上的浮点运算不是Exact Science :)。

如果您希望精确到预定义的小数位数,请使用Decimal而不是double或接受小间隔。

我实际上希望他们没有为浮点数和双精度数实现==运算符。 要问一个double或float是否等于任何其他值,几乎总是错误的做法。

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

例如,0.1和0.01的不可表示性(二进制)意味着尝试平方0.1的结果既不是0.01也不是最接近它的可表示数。

如果您需要机器对数字系统的解释(二进制),请仅使用浮点。 你不能代表10美分。

如果需要precision,请使用System.Decimal。 如果需要速度,请使用System.Double(或System.Float)。 浮点数不是“无限精度”数,因此断言相等必须包括容差。 只要您的数字具有合理的有效位数,就可以了。

  • 如果你想在非常大和非常小的数字上做数学运算,不要使用float或double。
  • 如果您需要无限精度,请不要使用float或double。
  • 如果要聚合非常多的值,请不要使用float或double(错误会自行复合)。
  • 如果您需要速度和尺寸,请使用float或double。

请参阅这个答案(也是我),详细分析精度如何影响数学运算的结果。

检查此问题的答案: 将浮点值的相等性检查为0是否安全?

真的,只需检查“在……的容忍范围内”

浮动和双打不能准确存储所有数字。 这是IEEE浮点系统的限制。 为了获得忠实的精确度,您需要使用更高级的数学库。

如果你不需要超过某一点的精度,那么也许十进制对你来说会更好。 它具有比双精度更高的精度。

对于类似的问题,我最终使用以下实现,这似乎是我的大多数测试用例(最多5位精度)的成功:

 public static double roundValue(double rawValue, double valueTick) { if (valueTick <= 0.0) return 0.0; Decimal val = new Decimal(rawValue); Decimal step = new Decimal(valueTick); Decimal modulo = Decimal.Round(Decimal.Divide(val,step)); return Decimal.ToDouble(Decimal.Multiply(modulo, step)); } 

有时结果比您对严格要求的结果更精确:FP IEEE 754.这是因为HW使用更多位进行计算。 请参阅C#规范和本文

Java有strictfp关键字,C ++有编译器开关。 我想念.NET中的这个选项