如何处理无限重复的数字作为小数?

时间:2013-04-18 23:07:20

标签: c# rounding rounding-error

当除法产生无限重复的数字时,数字显然会被截断以适合小数的大小。所以像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 个答案:

答案 0 :(得分:5)

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

  • 如果分数不为零,则删除分数的分子和分母中的相等数字
  • 数字正方形的平方根是数字本身
  • ...

因此,我们可以存储Fraction(1, 3)的实例,而不是将1/3存储在十进制/双精度值中,等于0.33333 ...然后我们可以定义这样的所有其他表达式来构建表达式而不是进行计算。最后,我们可以先用上面的规则简化表达式,然后计算结果。

我在网上搜索了一下,找到了这样做的图书馆,但还没找到。但我相信其他语言/平台甚至.NET都可以找到一些。

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

欢迎浮点运算的痛苦。

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

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

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

对于浮点数:

看看一些资源。首先,David Goldberg的经典着作“每个计算机科学家应该知道浮点运算”。这是摘要:

  

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

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

此外,原版应该可以在大砖建筑中随时可用。完整的引用是

  大卫戈德伯格。 1991.每个计算机科学家应该了解浮点数   算术。 ACM Comput。监测网。 23,1(1991年3月),5-48。 DOI = 10.1145 / 103162.103163   http://doi.acm.org/10.1145/103162.103163

然后看看这些资源:

答案 3 :(得分:0)

如果按照我commented的方式进行操作,那么将其存储到数据库中也不会有问题。您可以在db中使用Fraction类型存储它。参见UDT。