ActiveRecord'da Float ve Ondalık


283

Bazen, Activerecord veri türleri kafamı karıştırıyor. Err, sık sık. Sonsuz sorularımdan biri, belirli bir vaka için,

Kullanmalı mıyım :decimalyoksa :float?

Sık sık bu bağlantıya rastladım, ActiveRecord:: decimal vs: float? , ancak cevaplar kesin olmam için yeterince açık değil:

İnsanların asla şamandıra kullanmamaları ve her zaman ondalık kullanmaları için düz olarak önerdikleri birçok iplik gördüm. Ayrıca bazı insanlar tarafından sadece bilimsel uygulamalar için şamandıra kullanma önerileri gördüm.

İşte bazı örnek durumlar:

  • Coğrafi Konum / enlem / boylam: -45.756688, 120.5777777, ...
  • Oran / yüzdesi: 0.9, 1.25, 1.333, 1.4143, ...

:decimalGeçmişte kullandım , ama BigDecimalRuby'deki nesnelerle uğraşmanın bir şamandıra ile karşılaştırıldığında gereksiz bir şekilde garip olduğunu gördüm . Ayrıca :integerparayı / sentleri temsil etmek için kullanabileceğimi de biliyorum , ancak örneğin, zaman içinde hassasiyetin değişebileceği miktarlar gibi diğer durumlar için pek uygun değil.

  • Her birini kullanmanın avantajları / dezavantajları nelerdir?
  • Hangi türün kullanılacağını bilmek için bazı iyi kurallar neler olabilir?

Yanıtlar:


427

CompSci profesörümün para birimi için şamandıra kullanmamayı söylediğini hatırlıyorum.

Bunun nedeni, IEEE belirtiminin değişkenleri ikili biçimde tanımlamasıdır . Temel olarak, bir Float'ı temsil etmek için işaret, kesir ve üs depolar. İkili için bilimsel bir gösterim gibi (bir şey gibi +1.43*10^2). Bu nedenle, kesir ve ondalık sayıları Float'ta tam olarak saklamak imkansızdır.

Bu yüzden Ondalık biçim vardır. Eğer bunu yaparsan:

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

oysa sadece yaparsan

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

Bu nedenle, faizleri birleştirmek gibi küçük kesirler veya hatta coğrafi konumlandırma ile uğraşıyorsanız, ondalık formatta 1.0/10tam olarak 0.1 olduğundan Ondalık biçimini şiddetle tavsiye ederim .

Bununla birlikte, daha az doğru olmasına rağmen, şamandıraların daha hızlı işlendiğine dikkat edilmelidir. İşte bir kıyaslama:

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

Cevap

Hassasiyeti fazla önemsemediğinizde şamandıra kullanın . Örneğin, bazı bilimsel simülasyonlar ve hesaplamalar en fazla 3 veya 4 önemli basamağa ihtiyaç duyar. Bu, hız hassasiyetinde işlem yapmada yararlıdır. Hız kadar hassasiyete ihtiyaç duymadıkları için şamandıra kullanırlardı.

Kesin olması gereken sayılarla uğraşıyorsanız ve doğru sayıyı toplayarak (faizleri ve parayla ilgili şeyleri birleştirmek gibi) ondalık kullanın . Unutmayın: hassasiyete ihtiyacınız varsa, her zaman ondalık basamak kullanmalısınız.


Eğer doğru anlarsam şamandıra taban-2'de, ondalık taban-10'da mı? Şamandıra için iyi bir kullanım nedir? Örneğiniz ne yapıyor ve gösteriyor?
Jonathan Allard

1
Şunu yapmayın +1.43*2^10ziyade +1.43*10^2?
Cameron Martin

47
Gelecekteki ziyaretçiler için, para birimi için en iyi veri türü ondalık değil tamsayıdır. Alanın hassasiyeti pennies ise, alan pennies cinsinden bir tamsayı olacaktır (dolar cinsinden ondalık değil). Bir bankanın BT departmanında çalıştım ve orada böyle yapıldı. Bazı alanlar daha yüksek doğruluktaydı (kuruşun yüzde biri gibi) ama yine de tamsayılardı.
adg

1
@adg doğru: bigdecimal para birimi için de kötü bir seçimdir.
Eric Duminil

1
@adg haklısın. Son birkaç yıldır bazı muhasebe ve finansal uygulamalarla çalışıyorum ve tüm para birimi alanlarımızı tamsayı sütunlarında saklıyoruz. Bu davalar için çok daha güvenli.
Guilherme Lages Santos

19

Rails 3.2.18, SQLServer kullanırken: ondalık: integer olur, ancak SQLite iyi çalışır. Şamandıra geçmek bizim için bu sorunu çözdü.

Öğrenilen ders "her zaman homojen geliştirme ve dağıtım veritabanlarını kullanın!"


3
İyi bir nokta, 3 yıl sonra Rails yaptıktan sonra yürekten katılıyorum.
Jonathan Allard

3
"her zaman homojen geliştirme ve dağıtım veritabanları kullanın!"
zx1986

15

Rails 4.1.0'da, MySql veritabanına enlem ve boylamı kaydetmede sorunla karşılaştım. Kayan noktalı veri türüyle büyük kesir sayısını kaydedemez. Ve veri türünü ondalık olarak değiştiriyorum ve benim için çalışıyorum.

  def değişikliği
    change_column: şehirler,: enlem,: ondalık,: hassasiyet => 15,: ölçek => 13
    change_column: şehirler,: boylam,: ondalık,: hassasiyet => 15,: ölçek => 13
  son

Postgres'te yüzen: enlem ve: boylamımı saklıyorum ve bu gayet iyi çalışıyor.
Scott W

3
@Robikul: evet, bu iyi, ama aşırı. decimal(13,9) enlem ve boylam için yeterlidir. @ScottW: Hatırlamıyorum, ancak Postgres IEEE şamandıraları kullanıyorsa, sorun yaşamadığınız için sadece "iyi çalışır" ... EVET. Enlem ve boylam için yetersiz bir biçimdir. Sonunda en az önemli basamaklarda hatalar olacaktır.
Lonny Eachus

@LonnyEachus IEEE floatlarını lat / longs için yetersiz kılan nedir?
Alexander Suraphel

3
@AlexanderSuraphel Ondalık enlem ve boylam kullanıyorsanız, bir IEEE şamandırası en az önemli basamaklardaki hatalara açıktır. Bu nedenle, enlem ve boylamınız 1 metrelik bir hassasiyete sahip olabilir, ancak 100 metre veya daha fazla hatalarınız olabilir. Bu özellikle hesaplamalarda kullanıyorsanız doğrudur.
Lonny Eachus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.