ActiveRecordのFloatとDecimal


283

時々、Activerecordデータタイプは私を混乱させます。エラー、よくあります。私の永遠の質問の1つは、特定のケースについて、

私が使用する必要があります:decimal:float

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

浮動小数点を使用せず、常に10進数を使用することをお勧めするスレッドがたくさんあります。また、フロートを科学的用途にのみ使用するという提案もいくつかあります。

いくつかのケース例を以下に示します。

  • ジオロケーション/緯度/経度:-45.756688120.5777777、...
  • 比率/割合:0.91.251.3331.4143、...

私は:decimal以前に使用しましたがBigDecimal、Ruby でオブジェクトを処理することは、フロートに比べて不必要に扱いにくいことに気付きました。:integerたとえば、お金やセントを表すために使用できることもわかっていますが、たとえば、精度が時間の経過とともに変化する可能性がある場合など、他のケースにはあまり適していません。

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

回答:


427

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桁の有効数字のみが必要です。これは、精度と速度のトレードオフに役立ちます。彼らは速度ほど精度を必要としないので、彼らはフロートを使うでしょう。

正確で合計する必要がある数値を扱う場合は、10進数を使用します(利息の複利や金銭に関連するものなど)。注意:精度が必要な場合は、常に10進数を使用する必要があります。


それで私が正しく理解すれば、浮動小数点は2進数で、10進数は10進数です。フロートの良い使い方は何でしょうか?あなたの例は何をし、実証しますか?
ジョナサンアラード2011

1
あなたは意味しない+1.43*2^10のではなく+1.43*10^2
Cameron Martin

46
今後の訪問者にとって、通貨の最適なデータ型は10進数ではなく整数です。フィールドの精度がペニーの場合、フィールドはペニーの整数になります(ドルの小数ではありません)。私は銀行のIT部門で働いていたので、そこで行われていました。一部のフィールドはより高い精度(100分の1ペニーなど)でしたが、それでも整数でした。
16

1
@adgは正しいです。bigdecimalも通貨としては不適切です。
Eric Duminil 2017年

1
@adgあなたは正しい。私は過去数年にわたっていくつかの会計および財務アプリケーションで作業しており、すべての通貨フィールドを整数列に格納しています。この場合ははるかに安全です。
ギルヘルメラジェスサントス

19

Rails 3.2.18では、SQLServerを使用すると:decimalが:integerに変わりますが、SQLiteでは問題なく機能します。:floatに切り替えると、この問題は解決されました。

学んだ教訓は、「常に同種の開発および配備データベースを使用すること」です。


3
良い点は、Railsをやってから3年後、私は心から同意します。
ジョナサンアラード2014年

3
「常に同種の開発および配備データベースを使用してください!」
zx1986

15

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

  def change
    change_column:cities、:latitude、:decimal、:precision => 15、:scale => 13
    change_column:cities、:longitude、:decimal、:precision => 15、:scale => 13
  終わり

私は自分の:latitudeと:longitudeをPostgresの浮動小数点数として保存します。これは問題なく動作します。
スコットW

3
@ロビクル:はい、それは良いですがやり過ぎです。 decimal(13,9) 緯度と経度には十分です。@ScottW:覚えていませんが、PostgresがIEEE floatを使用している場合、問題が発生していないため、「正常に機能する」だけです。緯度と経度の形式としては不十分です。最終的には、最下位桁にエラーが発生します。
Lonny Eachus

@LonnyEachus IEEE floatが緯度/経度に対して不十分な理由は何ですか?
Alexander Suraphel、2016年

3
@AlexanderSuraphel 10進の緯度と経度を使用している場合、IEEE floatは最下位桁のエラーの影響を受けやすくなります。したがって、緯度と経度の精度はたとえば1メートルですが、誤差は100メートル以上になる可能性があります。これは、計算でそれらを使用している場合に特に当てはまります。
Lonny Eachus 16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.