web-dev-qa-db-ja.com

ActiveRecordでの浮動小数点と10進数

時々、Activerecordデータ型は私を混乱させます。ええ、よく。私の永遠の質問の1つは、与えられたケースでは、

:decimal:floatのどちらを使うべきですか?

私はこのリンクに出くわすことがよくあります、ActiveRecord :: decimal vs:float?私が確信するのに十分なほど明確にする:

フロートを使わず、常に10進数を使うようにフラットアウトを推奨するスレッドがたくさんあります。科学的な用途にのみfloatを使用するという提案もあります。

これがいくつかの例です。

  • 地理的位置/緯度/経度:-45.756688120.5777777、...
  • 比率/パーセンテージ:0.91.251.3331.4143、...

私は過去に:decimalを使ったことがありますが、RubyでBigDecimalオブジェクトを扱うことはfloatと比較して不必要に厄介です。たとえば、:integerを使って金額/セントを表すことができることもわかっていますが、他の場合、たとえば精度が時間の経過とともに変わる可能性がある場合などには、あまり適していません。

  • それぞれを使用する利点/欠点は何ですか?
  • どのタイプを使うべきかを知るための良い経験則は何でしょうか。
263
Jonathan Allard

私はCompSci教授が通貨にフロートを使わないことを言ったのを覚えています。

その理由は、 IEEE仕様でフロートが定義されている がバイナリ形式でどうなっているかです。基本的には、Floatを表すための符号、分数、指数を格納します。それはバイナリの科学的表記法のようなものです(+1.43*10^2のようなもの)。そのため、小数部と小数部をFloatに正確に格納することは不可能です。

それがDecimalフォーマットがある理由です。あなたがこれをするならば:

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

複利、あるいは地理位置情報のように小さな小数を扱う場合は、10進数フォーマットでは1.0/10は正確に0.1なので、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

回答

精度をあまり気にしない場合は、floatを使用してください。たとえば、科学的なシミュレーションや計算の中には、最大3桁または4桁の有効数字しか必要としないものがあります。これは正確さとスピードのトレードオフに役立ちます。彼らはスピードほど正確さを必要としないので、彼らはfloatを使うでしょう。

正確である必要があり、正しい数になるように合計する必要がある数を扱う場合は10進数を使用します(複利とお金に関連するものなど)。覚えておいてください:あなたが精度を必要とするならば、あなたは常に10進数を使うべきです。

400
Iuri G.

Rails 3.2.18では、SQLServerを使うと:decimalは:integerに変わりますが、SQLiteではうまくいきます。 :floatに切り替えると、この問題は解決しました。

学んだ教訓は、「常に同種の開発データベースとデプロイメントデータベースを使用すること」です。

17
ryan0

Rails 4.1.0では、MySqlデータベースに緯度と経度を保存することに問題がありました。 floatデータ型では大きな小数を保存することはできません。そして私はデータ型を10進数に変更して私のために働いています。

 def change 
 change_column:都市、:緯度、:decimal、:precision => 15、:scale => 13 
 change_column:都市、:経度、:decimal、:精度=> 15、::scale => 13 
 end 
11
Rokibul Hasan