ActiveRecord中的Float vs Decimal

Jon*_*ard 274 floating-point types ruby-on-rails decimal rails-activerecord

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

我应该使用:decimal:float

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

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

以下是一些示例案例:

  • 地理位置/纬度/经度:-45.756688,120.5777777,...
  • 比/百分比:0.9,1.25,1.333,1.4143,...

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

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

Iur*_* G. 414

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

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

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

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

而如果你这样做

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

因此,如果您正在处理小部分,如复合兴趣,甚至地理位置,我强烈建议使用十进制格式,因为十进制格式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
Run Code Online (Sandbox Code Playgroud)

回答

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

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

  • 对于未来的访问者,货币的最佳数据类型是整数,而不是十进制.如果字段的精度是便士那么字段将是便士的整数(不是美元的小数).我在一家银行的IT部门工作,就是那里的工作方式.有些字段的精度更高(例如百分之一便士),但它们仍然是整数. (35认同)
  • 谢谢你清理我的答案.你是最棒的 (14认同)
  • 关于十进制对于货币来说是一个糟糕的选择的建议要么已经过时,要么是错误的:在像 postgres 这样的数据库中,它们具有精确的精度,并且工作方式与整数完全相同,只是它们为您提供了开箱即用的正确小数位置。如果您使用整数来表示货币,则意味着您现在将责任放在应用程序上来正确管理金额的缩放。在小数列中以 2 为单位模拟货币是完全可以接受的。请参阅 https://www.postgresql.org/docs/9.1/datatype-numeric.html。他们甚至在那里说“整数可以被认为是小数的小数位为零”。 (3认同)

rya*_*an0 18

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

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

  • 好的一点,3年后做Rails,我全心全意地同意. (3认同)
  • "始终使用同构开发和部署数据库!" (3认同)

Rok*_*san 12

在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

  • @AlexanderSuraphel如果您使用的是十进制纬度和经度,则IEEE浮点数很容易出现最低有效位数的错误。因此,例如,您的经度和纬度可能具有1米的精度,但是您可能会有100米或更高的误差。如果在计算中使用它们,则尤其如此。 (3认同)
  • @Robikul:是的,这很好,但有点矫枉过正.对于纬度和经度,"十进制(13,9)"就足够了.@ScottW:我不记得了,但如果Postgres使用IEEE浮点数,它只能"正常",因为你没有遇到问题......是的.纬度和经度格式不足.Yo最终会出现最低有效数字的错误. (2认同)