当除法产生一个无限重复的数字时,这个数字显然会被截断以适应小数的大小。因此,1/3就变成了0.3333333333333333333。如果我们将这个数字乘以3,我们得到的结果是0.999999999999999999,而不是1,如果分数的真值被保留下来的话。
这是MSDN文章中关于decimal的代码示例:
decimal dividend = Decimal.One;
decimal divisor = 3;
// The following displays 0.9999999999999999999999999999 to the console
Console.WriteLine(dividend/divisor * divisor); 在将值0.9999999999999999999与1进行相等比较时,这会导致问题。在没有精度损失的情况下,它们将是相等的,但当然在这种情况下,比较将导致错误。
人们通常是如何处理这个问题的?除了定义每个比较的误差范围之外,还有更优雅的解决方案吗?
发布于 2013-04-19 07:31:11
这是一个非常古老且广为人知的数值计算问题。您已经说过,您正在寻找一种解决方案,而不是定义每个比较的误差范围。我想到的一种方法是先在内存中构建数学表达式树,然后再进行计算。有了这一点,我们可以在进行计算之前使用一些已知规则进行一些简化。规则如下:
因此,不是将1/3存储在等于0.33333的十进制/双精度值中……我们可以存储Fraction(1, 3)的一个实例。然后,我们可以定义所有其他类似的表达式来构建表达式,而不是进行计算。最后,我们可以先用上面的规则简化表达式,然后计算结果。
我在网上简单地搜索了一下,想找到可以这样做的库,但是还没有找到。但我相信可以找到一些适用于其他语言/平台甚至.NET的。
请注意,上述方法最终只是得到了更好的结果,但并没有解决数值计算本质上的这个问题。
发布于 2013-04-19 07:43:00
正如你所提到的,使用浮点数的计算结果必须‘适合’到浮点表示中。这就是为什么精确比较不是一个好主意--需要一定的容忍度。所以应该使用Math.Abs(x - y) < tolerance,而不是x == y。
发布于 2013-04-19 07:34:41
欢迎来到浮点算术之痛。
当然,您真正想要的是一个有理数类和库。这里是C#的一个开始,尽管我不知道它有多完整(可能不是很完整):
http://www.codeproject.com/Articles/88980/Rational-Numbers-NET-4-0-Version-Rational-Computin
在C/C++中有一些,但同样,不知道它们有多有用/多完整。
对于浮点数:
请看一些参考资料。首先,David Goldberg的经典著作“每个计算机科学家都应该知道浮点算术”。摘要如下:
浮点运算被许多人认为是深奥的学科。这相当令人惊讶,因为浮点在计算机系统中无处不在:几乎每种语言都有浮点数据类型;从PC到超级计算机的计算机都有浮点加速器;大多数编译器将时不时地被调用来编译浮点算法;并且几乎每个操作系统都必须响应浮点异常,例如overflow。本文提供了一个关于浮点方面的教程,这些方面对计算机系统的设计者有直接的影响。它从浮点表示和舍入误差的背景开始,继续讨论IEEE浮点标准,并以计算机系统构建者如何更好地支持浮点的示例结束。
您可以从以下几个位置免费下载该论文的编辑版本:
上找到
而且,原版应该在有所有书籍的大砖房里随时都能买到。完整的引用是
以上是
新闻的大卫·戈德堡带来的报道。1991年。每个计算机科学家都应该知道的浮点算术。ACM Comput。Surv。23,1(1991年3月),5-48。DOI=10.1145/103162.103163 http://doi.acm.org/10.1145/103162.103163
然后看看这些资源:
https://stackoverflow.com/questions/16094577
复制相似问题