【问题标题】:Working with decimals in Ruby on Rails 3在 Ruby on Rails 3 中使用小数
【发布时间】:2011-01-19 01:17:00
【问题描述】:

我正在尝试计算产品的平均净价。我的产品模型中有:total_sold 和 :total_net_revenue。在方法中进行直线除法似乎总是导致 0。我使用 BigDecimal,因为我认为这是问题所在......但是使用下面代码的最新迭代,当答案出来时我仍然为零一个小数。

def avg_price
  BigDecimal(total_sold.to_s) / (BigDecimal(total_net_revenue.to_s) / 100)
end  

净收入以美分为单位,这就是我除以 100 的原因。有人能指出我做错了什么或应该做什么吗?

【问题讨论】:

  • 为什么是BigDecimal?这些值末尾的to_f 不够吗?

标签: ruby-on-rails ruby ruby-on-rails-3


【解决方案1】:
total_net_revenue / total_sold

total_net_revenue / total_sold / 100.0

total_net_revenue.to_f / total_sold / 100

如果您愿意,这三种方法可以提高精度。请记住,“平均价格”是“平均价格/销售额。这是每件商品的金额,因此您需要按特定顺序进行划分。

【讨论】:

    【解决方案2】:

    第一:你分错了。

    100 件/150 美元 = 0.667 件/美元

    对比

    150 美元/100 件商品 = 每件商品 1.50 美元

    第二:与其他语言一样,您需要强制等式中的一个数字为小数,以便结果也被转换为 1。由于您的收入是一个整数,这意味着所有三个值都是整数,这意味着您得到了一个整数。要获得小数,请将其中一个转换为浮点数。

    换句话说,要得到你需要的东西,这样做:

    price_per_item = (total_net_revenue.to_f / 100) / total_sold
    

    【讨论】:

      【解决方案3】:

      你所做的称为整数除法,它会丢弃任何余数,因为它不能用整数表示,例如:

      1 / 3 # == 0
      

      正如其他受访者所提到的,您可以强制进行浮点除法。您需要通过调用 .to_f 来强制第一个参数为浮点数(此处为 1)。第二个参数将自动被强制转换为浮点数,即:

      1.to_f / 3 # ~ 0.3333...
      

      请注意,一旦转向浮点数,一般来说,结果不再准确。这就是为什么我放〜0.333。

      涉及更多的精确细节。我相信,在当今微处理器中常见的二进制浮点算术中,2 的幂仍然是精确的。但是,例如整数 3,不再精确表示,而只是在浮点表示的精度范围内(对于“双”精度,通常为 1E-16 左右)。

      长话短说,这里有一条经验法则:如果您处理的是货币价值,那么精度很重要(是否曾注意到电话账单上有 1 美分的差异?)不要存储计算结果,也不要' t 以浮点数存储值。而是使用整数或十进制数据类型(在内部存储字符串)。如果可能,仅计算浮点结果以用于显示和按需。一旦它们是浮点数,避免将大值和小值加在一起,并避免在浮点数中进行链式计算。返工你的代数以避免分裂直到最后。 Ruby 还支持 Rational 数据类型,它准确地表示分数并且可能很有用。

      这些问题属于“浮点错误传播”的科学范畴,如果需要,您可以在其中查找更多信息。

      【讨论】:

      • 感谢您的详细解释。我通常将货币值存储为美分并基于美分计算以避免小数,因为我确实需要不时存储这些值。
      • 这也是我发现通常效果最好的方法。还有一些宝石,例如金钱宝石,这为这种方法增添了一些便利。
      【解决方案4】:

      您需要将整美分(整数)的值转换为浮点数(带小数的数字),以便数学运算得到浮点数而不是整数。

      所以一般来说它是这样工作的:

      some_integer.to_f / some_other_integer.to_f  # returns a float
      

      【讨论】:

        【解决方案5】:

        即使其中一个值是浮点数,结果也会是浮点数。

        另外,如果你使用 Rails 和你的数据存储在特定模型中,ActiveRecord 有特殊的方法,你不需要自己计算。

        Model.average("field_with_data")
        

        还有可用的最小值、最大值、计数、求和方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-17
          • 1970-01-01
          • 2012-08-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多