10

当除法导致无限重复的数字时,该数字显然会被截断以适应小数的大小。所以像 1/3 这样的东西变成像 0.3333333333333333333 这样的东西。如果我们将该数字乘以 3,我们会得到类似 0.999999999999999999 的结果,而不是保留分数的真实值时得到的 1。

这是 MSDN 关于十进制的文章中的代码示例:

decimal dividend = Decimal.One;
decimal divisor = 3;
// The following displays 0.9999999999999999999999999999 to the console
Console.WriteLine(dividend/divisor * divisor); 

当将值 0.9999999999999999999 与 1 进行比较以获得相等性时,这会导致问题。如果没有精度损失,它们将是相等的,但是在这种情况下,比较当然会导致错误。

人们通常如何处理这个问题?除了为每次比较定义一些误差范围之外,还有更优雅的解决方案吗?

4

4 回答 4

5

这是一个非常古老且广为人知的数值计算问题。您已经说过您正在寻找一种解决方案,而不是为每次比较定义一些误差范围。我想到的一种方法是先在内存中构建数学表达式树,然后再进行计算。有了这个,我们可以在进行计算之前使用一些已知的规则进行一些简化。规则如:

  • 如果分数的分子和分母不为零,则删除它们中的相等数字
  • 一个数的平方根就是这个数本身
  • ...

因此,我们可以存储一个Fraction(1, 3). 然后我们可以像这样定义所有其他表达式来构建表达式而不是进行计算。最后我们可以先用上面的规则简化表达式,然后计算结果。

我简单地搜索了网络以找到执行此操作的库,但还没有找到。但我确信可以为其他语言/平台甚至 .NET 找到一些。

请注意,上述方法最终只会产生更好的结果,但并未解决数值计算本质中固有的问题。

于 2013-04-18T23:31:11.397 回答
2

正如您所提到的,使用浮点数的计算结果必须“拟合”到浮点表示中。这就是为什么精确比较不是一个好主意的原因——需要一定的容忍度。因此x == yMath.Abs(x - y) < tolerance应该使用 ,。

于 2013-04-18T23:43:00.150 回答
1

欢迎来到浮点运算的痛苦。

当然,您真正想要的是有理数类和库。这是 C# 中的一个开始,虽然我不知道它有多完整(可能不是很完整):

http://www.codeproject.com/Articles/88980/Rational-Numbers-NET-4-0-Version-Rational-Computin

C/C++ 中有一些,但同样,不知道它们有多有用/完整。

对于浮点数:

看看一些资源。首先,David Goldberg 的经典著作《每个计算机科学家都应该知道的关于浮点运算的知识》。这是摘要:

浮点运算被许多人认为是深奥的学科。这是相当令人惊讶的,因为浮点在计算机系统中无处不在:几乎每种语言都有浮点数据类型;从个人电脑到超级计算机的计算机都有浮点加速器;大多数编译器将不时被要求编译浮点算法;几乎每个操作系统都必须响应浮点异常,例如溢出。本文介绍了对计算机系统设计者有直接影响的浮点方面的教程。它从浮点表示和舍入误差的背景开始,继续讨论 IEEE 浮点标准,最后以计算机系统构建者如何更好地支持浮点的示例结束。

您可以从以下几个地方免费下载该论文的编辑版本:

此外,原件应该在带有所有书籍的大砖房中随时可用。完整的引用是

大卫·戈德堡。1991. 每个计算机科学家都应该知道的关于浮点运算的知识。ACM 计算。生存。23,1(1991 年 3 月),5-48。DOI=10.1145/103162.103163 http://doi.acm.org/10.1145/103162.103163

然后看看这些资源:

于 2013-04-18T23:34:41.013 回答
0

如果您按照我评论的方式进行,那么将其存储到数据库中也不会成为问题。您可以将其以分数类型存储在数据库中。请参阅 UDT。

于 2013-04-18T23:42:43.017 回答