Ruby浮点数学 - Sum Calc中的精度问题

时间:2012-05-28 13:29:40

标签: ruby-on-rails ruby validation math floating-point

早上好,

我在浮点数学方面遇到了一些问题,并且在“.to_f”,“* 100”和“.0”中完全丢失了!

我希望有人可以帮助我解决我的具体问题,并解释为什么他们的解决方案有效,以便我下次理解这一点。

我的程序需要做两件事:

  1. 求和小数列表,确定它们是否总和为1.0
  2. 确定1.0和数字之和之间的差异 - 将变量的值设置为精确差值,使总和等于1.0。
  3. 例如:

    1. [0.28,0.55,0.17] - >应该总和为1.0,但我一直得到1.xxxxxx。我正在以下列方式实现总和:

      sum = array.inject(0.0){|sum,x| sum+ (x*100)} / 100
      
    2. 我需要这个功能的原因是我正在阅读一组来自excel的小数。它们不是100%精确(它们缺少一些小数点)所以总和通常来自0.999999xxxxx或1.000xxxxx。例如,我将获得如下值:

      0.568887955,0.070564759,0.360547286
      
    3. 为了解决这个问题,我可以将前n-1个数字的总和,然后稍微改变最终数字,以便所有数字总和为1.0(必须使用上面的等式或任何我满足验证)以结束)。我目前正在实施如下:

                sum = 0.0
                array.each do |item|
                  sum += item * 100.0
                end
                array[i] = (100 - sum.round)/100.0
      

      我知道我可以通过注射来做到这一点,但是我试图用它来看看它有什么用。我认为这通常是有效的(从检查输出),但它并不总是符合上面的验证总和。所以,如果需要,我也可以调整这个。请注意,我在这些数字中只需要两个小数精度 - 即0.56而不是0.5623225。我可以在演示时或在计算期间将它们围绕下来......对我来说无关紧要。

      非常感谢你的帮助!

3 个答案:

答案 0 :(得分:9)

如果准确性对您很重要,则不应使用浮点值,根据定义,浮点值不准确。 Ruby有一些精确的数据类型,用于进行精度很重要的算术运算。根据您实际需要计算的内容,它们在我的头顶上BigDecimalRationalComplex

似乎在你的情况下,你正在寻找的是BigDecimal,它基本上是一个具有固定位数的数字,其中小数点后面有一个固定的位数(与浮动相反) point,小数点后面的任意位数。)

当您从Excel中读取并故意将这些字符串(如“0.9987”)转换为浮点数时,您将立即丢失字符串中包含的准确值。

require "bigdecimal"
BigDecimal("0.9987")

这个价值是准确的。它是0.9987。不是0.998732109,或任何接近它的东西,但是0.9987。您可以对其使用所有常用的算术运算。如果不将浮点混合到算术运算中,返回值将保持精确。

如果你的数组包含你从Excel得到的原始字符串(即你没有#to_f'),那么这将给你一个BigDecimal,它是它们之和与1的差值。 / p>

1 - array.map{|v| BigDecimal(v)}.reduce(:+)

答案 1 :(得分:2)

或者:

  • 继续使用花车和round(2)总计:12.341.round(2) # => 12.34

  • 使用整数(即美分而不是美元)

  • 使用BigDecimal,只要你从只有两位小数的BigDecimal开始,你就不需要在求和之后进行舍入。

答案 2 :(得分:0)

我认为算法与精确度和精度的关系远远超过选择IEEE浮点而非另一种表示法。

人们过去常常做一些精确的计算,同时还要处理精度和精度问题。他们通过管理他们使用的算法并理解如何更深入地表示函数来实现。我认为你可能会错误地抛弃那种更好的理解并假设另一种表现形式是解决方案。

例如,函数的多项式表示不会正确处理渐近线或奇点。

不要这么快丢弃浮点数。我可能会更聪明地使用它们的方式会很好。