ActiveRecord中的Float vs Decimal

时间:2011-12-15 01:49:53

标签: ruby-on-rails types floating-point decimal rails-activerecord

有时,Activerecord数据类型让我感到困惑。呃,经常。对于特定情况,我的一个永恒问题是

  

我应该使用:decimal还是:float

我经常遇到这个链接 ActiveRecord: :decimal vs :float? ,但答案不够清楚,我无法确定:

  

我见过许多线索,人们建议不要使用   浮点数并始终使用小数。我也看到过一些人的建议   人们只将浮法用于科学应用。

以下是一些示例案例:

  • 地理位置/纬度/经度:-45.756688120.5777777,...
  • 比例/百分比:0.91.251.3331.4143,...

我过去曾使用:decimal,但我发现与浮点数相比,处理Ruby中的BigDecimal对象是不必要的尴尬。例如,我也知道我可以使用:integer代表金钱/美分,但它并不适合其他情况,例如精确度随时间变化的数量。

  • 使用每种产品的优点/缺点是什么?
  • 知道使用哪种类型会有什么好的经验法则?

3 个答案:

答案 0 :(得分:397)

我记得我的CompSci教授说永远不会使用花车作为货币。

原因是二进制格式的IEEE specification defines floats。基本上,它存储符号,分数和指数来表示浮点数。它就像二进制的科学记法(类似+1.43*10^2)。因此,不可能在Float中精确存储分数和小数。

这就是为什么有一个十进制格式。如果你这样做:

irb:001:0> "%.47f" % (1.0/10)
=> "0.10000000000000000555111512312578270211815834045" # not "0.1"!

而如果你这样做

irb:002:0> (1.0/10).to_s
=> "0.1" # the interprer rounds the number for you

因此,如果您正在处理小部分,例如复合利益,甚至地理位置,我强烈推荐使用十进制格式,因为十进制格式1.0/10正好是0.1。

然而,应该注意的是,尽管不太准确,浮子的处理速度更快。这是一个基准:

require "benchmark" 
require "bigdecimal" 

d = BigDecimal.new(3) 
f = Float(3)

time_decimal = Benchmark.measure{ (1..10000000).each { |i| d * d } } 
time_float = Benchmark.measure{ (1..10000000).each { |i| f * f } }

puts time_decimal 
#=> 6.770960 seconds 
puts time_float 
#=> 0.988070 seconds

答案

如果您不太关心精度,请使用 float 。例如,一些科学模拟和计算仅需要3或4位有效数字。这在折衷速度准确性方面很有用。由于它们不像速度那样需要精度,因此它们会使用浮动。

如果您正在处理需要精确的数字并总结正确的数字(如复利和与金钱相关的事情),请使用十进制。请记住:如果您需要精确度,那么您应该始终使用小数。

答案 1 :(得分:17)

在Rails 3.2.18中,:decimal在使用SQLServer时变为:integer,但它在SQLite中工作正常。切换到:float为我们解决了这个问题。

吸取的教训是“始终使用同构开发和部署数据库!”

答案 2 :(得分:10)

在Rails 4.1.0中,我遇到了将纬度和经度保存到MySql数据库的问题。它不能用float数据类型保存大的分数。我将数据类型更改为十进制并为我工作。

  def change
    change_column :cities, :latitude, :decimal, :precision => 15, :scale => 13
    change_column :cities, :longitude, :decimal, :precision => 15, :scale => 13
  end