浮动与ActiveRecord十进制

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

我应该使用:decimal还是:float

我经常遇到这个链接, ActiveRecord :: decimal与:float? ,但答案还不够清楚,我可以肯定:

我已经看到许multithreading,人们build议从不使用浮动,并始终使用十进制。 我也看到一些人build议只使用float来进行科学应用。

以下是一些示例:

  • 地理位置/经度/纬度: 120.5777777 ,…
  • 比率/百分比: 1.4143 ,…

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

  • 使用每个的优点/缺点是什么?
  • 要知道使用哪种types,有什么好的经验法则?

我记得我的CompSci教授说永远不要使用浮动货币。

其原因是IEEE规范如何定义二进制格式的浮点数 。 基本上,它存储符号,分数和指数来表示一个浮点数。 这就像二进制的科学符号(类似于+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。

但是,应该指出的是,尽pipe不太准确,浮游物处理速度更快。 这是一个基准:

 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 3.2.18中,:decimal在使用SQLServer时变成:integer,但是在SQLite中它工作正常。 切换到:float为我们解决了这个问题。

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

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

   def变化
     change_column:cities,:latitude,:decimal,:precision => 15,:scale => 13
     change_column:cities,:longitude,:decimal,:precision => 15,:scale => 13
  结束