2011-01-19 13 views
11

Sto cercando di calcolare il prezzo netto medio di un prodotto. Ho, nel mio modello Product: total_sold e: total_net_revenue. Fare una divisione diritta nel metodo sembra sempre risultare in 0. Ho fatto ricorso a BigDecimal perché pensavo che fosse il problema ... ma con la mia ultima iterazione del codice qui sotto, sto ancora ottenendo zero quando la risposta viene fuori un decimale.Utilizzo di decimali in Ruby on Rails 3

def avg_price 
    BigDecimal(total_sold.to_s)/(BigDecimal(total_net_revenue.to_s)/100) 
end 

ricavo netto è in centesimi, che è il motivo per cui divido per 100. Qualcuno può sottolineare quello che sto facendo di sbagliato o dovrei fare?

+2

Perché un 'BigDecimal'? Non sarebbe sufficiente? To_f' alla fine di questi valori? –

risposta

13
total_net_revenue/total_sold 

o

total_net_revenue/total_sold/100.0 

o

total_net_revenue.to_f/total_sold/100 

Questi tre metodi danno una quantità crescente di precisione, se lo vuoi. Ricorda che "prezzo medio" è "prezzo medio/la vendita che sono soldi-per-oggetto così si vuole fare la divisione in questo ordine specifico

12

Primo:.. Stai dividendo il modo sbagliato

. 100 oggetti/$ 150 = 0,667 articoli per dollaro

vs.

$ 150/100 articoli = $ 1,50 per voce

In secondo luogo: come gli altri la nguage, è necessario forzare uno dei numeri nell'equazione per essere un decimale in modo che il risultato sia lanciato come uno. Poiché le tue entrate sono un numero intero, significa che tutti e tre i valori erano numeri interi, il che significa che hai ottenuto un numero intero come risultato. Per ottenere un decimale, lanciare uno di essi come numero a virgola mobile.

In altre parole, per ottenere quello che ti serve, fare questo:

price_per_item = (total_net_revenue.to_f/100)/total_sold 
3

È necessario convertire i valori che sono in centesimi interi (interi) per carri (numeri con decimali) in modo che la matematica sarà genera un numero mobile invece di un intero.

Quindi, in generale funziona in questo modo:

some_integer.to_f/some_other_integer.to_f # returns a float 
8

quello che stai facendo è chiamata divisione intera, che scarta qualsiasi residuo, in quanto non sarebbe esprimibili in numeri interi, ad esempio:

1/3 # == 0 

Come altri intervistati hanno menzionato, è possibile forzare una divisione in virgola mobile. Devi forzare il primo argomento ad essere un float (1 qui) chiamando .to_f. Il secondo argomento sarà automaticamente costretto a un galleggiante, cioè .:

1.to_f/3 # ~ 0.3333... 

Nota che una volta che ci si sposta a numeri in virgola mobile, il risultato, in generale, non è più esatto. Questo è il motivo per cui ho messo ~ 0.333.

I dettagli precisi sono più coinvolti. Nell'aritmetica in virgola mobile binaria, che è comune nei microprocessori di oggi, le potenze di 2 sono ancora esatte, credo.Ma l'intero 3, ad esempio, non è più rappresentato esattamente, ma solo all'interno della precisione della rappresentazione in virgola mobile (tipicamente 1E-16 o circa per precisione "doppia").

Per farla breve, ecco una regola empirica: se hai a che fare con i valori monetari, qualora una materia di precisione (? Mai stato notato una discrepanza 1 centesimo su una bolletta del telefono) Escludere risultati calcolati, e don memorizza i valori in punti mobili. Utilizzare invece i tipi di dati interi o decimali (che memorizzano internamente le stringhe). Calcola risultati in virgola mobile solo per la visualizzazione e su richiesta, se possibile. Evita di aggiungere valori grandi e piccoli insieme una volta che sono float ed evita calcoli concatenati nei float. Rileggi l'algebra per evitare le divisioni fino alla fine. Ruby supporta anche un tipo di dati Rational che rappresenta esattamente le frazioni e potrebbe essere utile.

Questi problemi rientrano nella scienza della "propagazione dell'errore in virgola mobile", che è dove è possibile cercare più informazioni se necessario.

+0

Grazie per la spiegazione dettagliata. Solitamente memorizzo i valori monetari come centesimi e calcoli in base ai centesimi per evitare i decimali perché è necessario archiviare tali valori di tanto in tanto. – Slick23

+0

Questo è quello che ho trovato funziona anche in generale. Ci sono anche alcune gemme, ad es. la gemma del denaro, che aggiunge una certa facilità a quell'approccio. –

0

Anche se uno dei valori è Float, il risultato sarà Float.

Anche se si utilizzano Rails e gli archivi dati in un modello specifico, ActiveRecord ha metodi speciali e non è necessario calcolare da soli.

Model.average("field_with_data") 

Disponibili anche metodi di minimo, massimo, conteggio, somma.