Ruby 浮点数学 - 求和计算中的精度问题

问题描述 投票:0回答:3

我在浮点数学方面遇到了一些问题,并且完全迷失在“.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。 我可以在演示时或在计算过程中将它们四舍五入......这对我来说并不重要。

非常感谢您的帮助!

ruby-on-rails ruby validation math floating-point
3个回答
13
投票

如果准确性对您很重要,则不应使用浮点值,根据定义,浮点值是不准确的。 Ruby 有一些精度数据类型,用于进行精度很重要的算术运算。 它们是,在我的脑海中,

BigDecimal
Rational
Complex
,具体取决于您实际需要计算的内容。

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

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

require "bigdecimal"
BigDecimal("0.9987")

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

如果您的数组包含从 Excel 获得的原始字符串(即您还没有对它们进行

#to_f
处理),那么这将为您提供一个 BigDecimal,它是它们的总和与 1 之间的差值。

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

3
投票

要么:

  • 继续使用浮动,

    round(2)
    您的总数:
    12.341.round(2) # => 12.34

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

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


-1
投票

我认为算法与准确度和精度的关系比选择 IEEE 浮点而不是其他表示形式有更多作用。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.