Float vs Decimal in ActiveRecord


283

A volte, i tipi di dati Activerecord mi confondono. Err, spesso. Una delle mie domande eterne è, per un dato caso,

Dovrei usare :decimalo :float?

Mi sono imbattuto spesso in questo link, ActiveRecord:: decimal vs: float? , ma le risposte non sono abbastanza chiare per essere sicuro:

Ho visto molti thread in cui le persone raccomandano di non usare mai il float e usare sempre i decimali. Ho anche visto suggerimenti di alcune persone sull'utilizzo di float solo per applicazioni scientifiche.

Ecco alcuni casi di esempio:

  • Geolocalizzazione / latitudine / longitudine: -45.756688, 120.5777777, ...
  • Rapporto / percentuale: 0.9, 1.25, 1.333, 1.4143, ...

Ho usato :decimalin passato, ma ho scoperto che occuparsi di BigDecimaloggetti in Ruby era inutilmente imbarazzante rispetto a un galleggiante. So anche che posso usare :integerper rappresentare denaro / centesimi, ad esempio, ma non si adatta perfettamente ad altri casi, ad esempio quando le quantità in cui la precisione potrebbe cambiare nel tempo.

  • Quali sono i vantaggi / gli svantaggi dell'utilizzo di ciascuno?
  • Quali sarebbero alcune buone regole pratiche per sapere quale tipo usare?

Risposte:


427

Ricordo il mio professore CompSci che diceva di non usare mai i float per la valuta.

Il motivo è il modo in cui la specifica IEEE definisce i float in formato binario. Fondamentalmente, memorizza segno, frazione ed esponente per rappresentare un galleggiante. È come una notazione scientifica per binario (qualcosa del genere +1.43*10^2). Per questo motivo, è impossibile memorizzare esattamente le frazioni e i decimali in Float.

Ecco perché esiste un formato decimale. Se lo fai:

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

mentre se lo fai e basta

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

Quindi, se hai a che fare con piccole frazioni, come gli interessi composti, o forse anche la geolocalizzazione, consiglio vivamente il formato decimale, poiché in formato decimale 1.0/10è esattamente 0,1.

Tuttavia, va notato che nonostante siano meno precisi, i float vengono elaborati più velocemente. Ecco un punto di riferimento:

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

Risposta

Usa float quando non ti importa troppo della precisione. Ad esempio, alcune simulazioni e calcoli scientifici richiedono solo fino a 3 o 4 cifre significative. Ciò è utile nel compromettere la precisione per la velocità. Dal momento che non hanno bisogno di precisione tanto quanto la velocità, userebbero il galleggiante.

Usa i decimali se hai a che fare con numeri che devono essere precisi e sommati al numero corretto (come gli interessi composti e le cose relative al denaro). Ricorda: se hai bisogno di precisione, dovresti sempre usare il decimale.


Quindi, se ho capito bene, float è in base-2 mentre decimale è in base-10? Quale sarebbe un buon uso per il galleggiante? Cosa fa e dimostra il tuo esempio?
Jonathan Allard,

1
Non intendi +1.43*2^10piuttosto che +1.43*10^2?
Cameron Martin,

47
Per i visitatori futuri, il miglior tipo di dati per la valuta è intero, non decimale. Se la precisione del campo è di centesimi, allora il campo sarebbe un numero intero in centesimi (non un decimale in dollari). Ho lavorato nel dipartimento IT di una banca ed è così che è stato fatto lì. Alcuni campi avevano una precisione maggiore (come centesimi di centesimo) ma erano ancora numeri interi.
adg

1
@adg ha ragione: bigdecimal è anche una cattiva scelta per la valuta.
Eric Duminil,

1
@adg hai ragione. Ho lavorato con alcune applicazioni contabili e finanziarie negli ultimi anni e archiviamo tutti i nostri campi di valuta in colonne intere. È molto più sicuro per questi casi.
Guilherme Lages Santos,

19

In Rails 3.2.18,: il decimale si trasforma in: intero quando si utilizza SQL Server, ma funziona bene in SQLite. Passando a: float ha risolto questo problema per noi.

La lezione appresa è "utilizzare sempre database di sviluppo e distribuzione omogenei!"


3
Buon punto, 3 anni dopo di fare Rails, sono pienamente d'accordo.
Jonathan Allard,

3
"usa sempre database di sviluppo e distribuzione omogenei!"
zx1986,

15

In Rails 4.1.0 ho riscontrato problemi con il salvataggio di latitudine e longitudine nel database MySql. Non può salvare un numero di frazione elevato con il tipo di dati float. E cambio il tipo di dati in decimale e funzionante per me.

  cambio di def
    change_column: città,: latitudine,: decimale,: precisione => 15,: scala => 13
    change_column: città,: longitudine,: decimale,: precisione => 15,: scala => 13
  fine

Salvo il mio: latitudine e: longitudine come galleggia in Postgres, e funziona perfettamente.
Scott W,

3
@Robikul: sì, va bene, ma eccessivo. decimal(13,9) è sufficiente per latitudine e longitudine. @ScottW: Non ricordo, ma se Postgres utilizza i float IEEE, funziona "bene" solo perché non hai riscontrato problemi ... ANCORA. È un formato insufficiente per latitudine e longitudine. Yo alla fine avrà errori nelle cifre meno significative.
Lonny Eachus,

@LonnyEachus cosa rende i galleggianti IEEE insufficienti per lat / longs?
Alexander Suraphel,

3
@AlexanderSuraphel Se si utilizzano latitudine e longitudine decimali, un float IEEE è suscettibile di errori nelle cifre meno significative. Quindi la tua latitudine e longitudine potrebbero avere una precisione di 1 metro per esempio, ma potresti avere errori di 100 metri o più. Ciò è particolarmente vero se li stai usando nei calcoli.
Lonny Eachus il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.