1. ホーム
  2. ruby-on-rails

[解決済み】ActiveRecordのFloatとDecimalの比較

2022-03-30 19:47:59

質問

Activerecordのデータ型に戸惑うことがあります。いや、よくあることです。私の永遠の疑問の1つは、あるケースについてです。

を使うべきでしょうか? :decimal または :float ?

このリンクによく出くわしました。 ActiveRecord: :decimal vs :float? しかし、その答えは、私が確信できるほど明確ではありません。

というスレッドをよく見かけます。 floatを使用し、常に10進数を使用します。また、いくつかの提案も目にしました。 科学的な用途にのみfloatを使用するようにとのことです。

以下はその例です。

  • ジオロケーション/緯度/経度。 -45.756688 , 120.5777777 , ...
  • 比率/パーセンテージ。 0.9 , 1.25 , 1.333 , 1.4143 , ...

私は :decimal を扱うのは大変でした。 BigDecimal オブジェクトは、float に比べて不必要に厄介です。また :integer は、たとえばお金やセントを表現するのに使いますが、他のケース、たとえば時間の経過とともに精度が変化するような量には、あまり適していません。

  • それぞれを使うメリット/デメリットは何ですか?
  • どのタイプを使うべきか、経験則として何か良い方法はありますか?

どのように解決するのですか?

CompSciの教授が通貨にfloatを使うなと言っていたのを覚えています。

その理由は、どのように IEEE仕様では浮動小数点が定義されている を2進数で表したものです。基本的には、符号、分数、指数を格納して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です。

ただし、精度が低いにもかかわらず、浮動小数点数の方が処理速度が速いことには注意が必要です。以下はベンチマークです。

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桁までしか必要ない場合があります。これは、精度とスピードをトレードオフするのに有効です。速さほど精度を必要としないので、floatを使うのでしょう。

使用方法 10進数 正確な数字が必要な場合(複利やお金に関すること)。正確さが必要な場合は、常に10進数を使用することを忘れないでください。