web-dev-qa-db-fra.com

Float vs Decimal dans ActiveRecord

Parfois, les types de données Activerecord me déroutent. Euh, souvent. Une de mes questions éternelles est, pour un cas donné,

Devrais-je utiliser :decimal ou :float?

J'ai souvent rencontré ce lien, ActiveRecord:: décimal vs: float?, mais les réponses ne sont pas tout à fait assez clair pour que je sois certain:

J'ai vu de nombreuses discussions où les gens recommandent de ne jamais utiliser float et d'utiliser toujours décimal. Certaines personnes ont également suggéré d'utiliser float pour des applications scientifiques uniquement.

Voici quelques exemples de cas:

  • Géolocalisation/latitude/longitude: -45.756688, 120.5777777, ...
  • Rapport/pourcentage: 0.9, 1.25, 1.333, 1.4143, ...

J'ai déjà utilisé :decimal, mais j’ai trouvé que traiter avec BigDecimal objets dans Ruby était inutilement gênant par rapport à un float. Je sais aussi que je peux utiliser :integer pour représenter argent/cents, par exemple, mais cela ne convient pas pour d'autres cas, par exemple lorsque des quantités dans lesquelles la précision peut changer avec le temps.

  • Quels sont les avantages/inconvénients de l’utilisation de chacun?
  • Quelles seraient les bonnes règles empiriques pour savoir quel type utiliser?
269
Jonathan Allard

Je me souviens que mon professeur de CompSci avait dit de ne jamais utiliser de flotteurs comme monnaie.

La raison en est la façon dont la spécification IEEE définit les flottants au format binaire. Fondamentalement, il stocke le signe, la fraction et l’exposant pour représenter un flottant. C'est comme une notation scientifique pour le binaire (quelque chose comme +1.43*10^2). A cause de cela, il est impossible de stocker des fractions et des nombres décimaux dans Float exactement.

C'est pourquoi il existe un format décimal. Si tu fais ça:

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

alors que si vous faites juste

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

Donc, si vous avez affaire à de petites fractions, telles que des intérêts composés, ou peut-être même une géolocalisation, je vous recommande vivement le format Decimal, car au format décimal, 1.0/10 vaut exactement 0,1.

Cependant, il convient de noter que, bien qu’ils soient moins précis, les flotteurs sont traités plus rapidement. Voici un repère:

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

Répondre

Utilisez float lorsque vous ne vous souciez pas trop de la précision. Par exemple, certaines simulations et calculs scientifiques ne nécessitent que 3 ou 4 chiffres significatifs. Ceci est utile pour échanger de la précision pour la vitesse. Comme ils n'ont pas besoin de précision, ils utiliseraient float.

Utilisez décimal si vous traitez avec des nombres qui doivent être précis et résumer pour obtenir un nombre correct (comme des intérêts composés et des choses liées à l'argent). Rappelez-vous: si vous avez besoin de précision, vous devez toujours utiliser décimal.

404
Iuri G.

Dans Rails 3.2.18,: le point décimal se transforme en: entier lors de l'utilisation de SQLServer, mais cela fonctionne correctement sous SQLite. Passer à: float a résolu ce problème pour nous.

La leçon à retenir est "utilisez toujours des bases de données de développement et de déploiement homogènes!"

18
ryan0

Dans Rails 4.1.0, j'ai eu du mal à enregistrer la latitude et la longitude dans la base de données MySql. Il ne peut pas enregistrer un grand nombre de fractions avec le type de données float. Et je change le type de données en décimal et travaille pour moi.

 def change 
 change_column: villes,: latitude,: décimal,: précision => 15,: échelle => 13 
 change_column: villes,: longitude,: décimal,: précision => 15,: échelle => 13 
 Fin 
11
Rokibul Hasan