7

大家早上好,

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

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

我的程序需要做两件事:

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

例如:

  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
    

为了解决这个问题,我可以取前 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。我可以在演示时或在此计算期间将它们四舍五入……这对我来说并不重要。

非常感谢您的帮助!

4

3 回答 3

11

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

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

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

require "bigdecimal"
BigDecimal("0.9987")

该值是精确的。它是 0.9987。不是 0.998732109 或任何接近它的值,而是 0.9987。您可以对其使用所有常用的算术运算。如果您不将浮点数混入算术运算中,则返回值将保持精确。

如果您的数组包含您从 Excel 获得的原始字符串(即您没有#to_f'd 它们),那么这将为您提供一个 BigDecimal,它是它们之和与 1 之间的差异。

1 - array.map{|v| BigDecimal(v)}.reduce(:+)
于 2012-05-28T14:01:54.970 回答
2

任何一个:

  • 继续使用浮点数和round(2)你的总数:12.341.round(2) # => 12.34

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

  • 使用 BigDecimal 并且你不需要在对它们求和后进行四舍五入,只要你从 BigDecimal 开始只有两位小数。

于 2012-05-28T14:01:22.433 回答
0

我认为算法与准确性和精度的关系比选择 IEEE 浮点数而不是另一种表示法有很大关系。

人们过去常常在处理准确性和精度问题的同时进行一些精细计算。他们会通过管理他们使用的算法并理解如何更深入地表示函数来做到这一点。我认为你可能会犯错误,抛开更好的理解并假设另一种表示是解决方案。

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

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

于 2012-05-28T16:35:54.257 回答