Double precision problems on .NET
我有一个简单的C函数:
1 2 3 4 | public static double Floor(double value, double step) { return Math.Floor(value / step) * step; } |
它计算的是大于或等于"value"的数字,即"step"的倍数。但它缺乏精确性,如以下测试所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [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"(double),所以如果我更改了这个后缀,就可以获得更高的精度。
实际上,我希望他们没有实现float和double的==操作符。询问double或float是否等于任何其他值几乎总是错误的。
计算机上的浮点运算不是一门精确的科学。
如果要精确到预定的小数位数,请使用十进制而不是双精度,或者接受较小的间隔。
如果省略了所有的F后缀(即
但对于其余的问题,我同意其他关于比较双精度值或浮点值是否相等的答案,这只是不可靠。
如果需要精度,请使用System.Decimal。如果需要速度,请使用System.Double(或System.Float)。浮点数不是"无限精度"数字,因此断言相等必须包含一个公差。只要你的数字有一个合理的有效位数,这是可以的。
- 如果你想对非常大和非常小的数字进行数学运算,不要使用浮点或双精度。
- 如果需要无限精度,不要使用浮点或双精度。
- 如果要聚合大量的值,请不要使用float或double(错误会自行复合)。
- 如果您需要速度和大小,请使用浮动或双精度。
有关精度如何影响数学运算结果的详细分析,请参阅此答案(我也是)。
http://en.wikipedia.org/wiki/floating_point准确性问题
For example, the non-representability of 0.1 and 0.01 (in binary) means that the result of attempting to square 0.1 is neither 0.01 nor the representable number closest to it.
只有在需要机器对数字系统进行解释(二进制)时才使用浮点。你不能代表10美分。
检查这个问题的答案:检查浮点值是否等于0安全?
真的,只要检查"在……的容忍范围内"
浮点数和双精度数不能准确存储所有数字。这是IEEE浮点系统的一个限制。为了获得可靠的精度,您需要使用更高级的数学库。
如果您不需要精度超过某一点,那么也许十进制会更好地为您工作。它比双精度高。
有时结果比您从严格的标准期望的更精确:fp ieee 754。这是因为硬件使用更多的位进行计算。参见C规范和本文
Java具有StuttFP关键字,C++具有编译器开关。我错过了.NET中的那个选项
对于类似的问题,我最终使用了以下实现,这似乎成功了我的大多数测试用例(高达5位精度):
1 2 3 4 5 6 7 8 9 10 |