有时,Activerecord 数据类型让我感到困惑。错误,经常。我永恒的问题之一是,对于一个特定的案例,
我应该使用:decimalor:float吗?
:decimal
:float
我经常遇到这个链接, ActiveRecord: :decimal vs :float? ,但答案还不够清楚,我无法确定:
我见过许多线程,人们建议完全不要使用浮点数并始终使用小数。我还看到一些人建议仅将 float 用于科学应用。
以下是一些示例案例:
-45.756688
120.5777777
0.9
1.25
1.333
1.4143
我过去使用:decimal过,但我发现在 Ruby 中处理BigDecimal对象与浮点数相比是不必要的尴尬。例如,我也知道我可以:integer用来表示钱/美分,但它不太适合其他情况,例如精度可能随时间变化的数量。
BigDecimal
:integer
我记得我的 CompSci 教授说永远不要使用浮点数作为货币。
原因是IEEE 规范如何以二进制格式定义浮点数。基本上,它存储符号、分数和指数来表示浮点数。这就像二进制的科学记数法(类似于+1.43*10^2)。因此,不可能将分数和小数精确地存储在 Float 中。
+1.43*10^2
这就是为什么有十进制格式的原因。如果你这样做:
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。
1.0/10
但是,应该注意的是,尽管精度较低,但浮点数的处理速度更快。这是一个基准:
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 位有效数字。这在权衡准确性以换取速度时很有用。由于他们不需要像速度一样多的精度,他们会使用浮点数。
如果您正在处理需要精确并总结为正确数字的数字(例如复利和与金钱相关的事物),请使用 小数。 请记住:如果您需要精度,那么您应该始终使用小数。