有时,Activerecord数据类型让我感到困惑。经常犯错,。我的一个永恒的问题是,对于一个给定的情况,
我应该使用:decimal还是:float?
我经常遇到这个链接,ActiveRecord::decimal vs:float?,但答案还不够明确,我无法确定:
我见过很多人极力建议不要使用的线程
浮点数,总是使用小数。我也看到了一些建议
人们只在科学应用中使用浮子。
以下是一些例子:
地理位置/纬度/经度:-45.756688,120.5777777,…
比例/百分比:0.9,1.25,1.333,1.4143,…
我过去使用过:decimal,但我发现在Ruby中处理BigDecimal对象比处理float对象更尴尬。例如,我还知道我可以使用:integer来表示钱/分,但它不太适合其他情况,例如当精度随时间变化的数量时。
使用每种方法的优点/缺点是什么?
有什么好的经验法则来知道使用哪种类型?
我记得我的CompSci教授说永远不要使用浮动货币。
原因是IEEE规范以二进制格式定义浮点数。基本上,它存储符号、分数和指数来表示Float。它就像二进制的科学符号(比如+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
回答
当你不太关心精度时,使用浮点数。例如,一些科学模拟和计算只需要3或4位有效数字。这在以速度换取准确性方面很有用。由于他们不像速度那样需要精度,他们会使用浮点数。
如果你处理的数字需要精确和正确的数字(比如复利和与钱有关的东西),可以使用十进制。记住:如果你需要精度,那么你应该总是使用十进制。
在Rails 4.1.0中,我遇到了将经纬度保存到MySql数据库的问题。对于浮点型数据,不能保存大分数数。我把数据类型改为十进制,这对我很有用。
def change
change_column :cities, :latitude, :decimal, :precision => 15, :scale => 13
change_column :cities, :longitude, :decimal, :precision => 15, :scale => 13
end
我记得我的CompSci教授说永远不要使用浮动货币。
原因是IEEE规范以二进制格式定义浮点数。基本上,它存储符号、分数和指数来表示Float。它就像二进制的科学符号(比如+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
回答
当你不太关心精度时,使用浮点数。例如,一些科学模拟和计算只需要3或4位有效数字。这在以速度换取准确性方面很有用。由于他们不像速度那样需要精度,他们会使用浮点数。
如果你处理的数字需要精确和正确的数字(比如复利和与钱有关的东西),可以使用十进制。记住:如果你需要精度,那么你应该总是使用十进制。