2011-01-19 57 views
11

Estoy tratando de calcular el precio neto promedio de un producto. Tengo, en mi modelo de Producto: total_sold y: total_net_revenue. Hacer una división directa en el método parece siempre dar como resultado 0. Recurrí al uso de BigDecimal porque pensé que ese era el problema ... pero con mi última iteración del código a continuación, sigo obteniendo cero cuando me llega la respuesta. un decimal.Trabajando con decimales en Ruby on Rails 3

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

Los ingresos netos se encuentra en centavos de dólar, por lo que divido por 100. Puede alguien señalar lo que estoy haciendo mal o debería hacer?

+2

¿Por qué un 'BigDecimal'? ¿No bastaría 'to_f' al final de estos valores? –

Respuesta

13
total_net_revenue/total_sold 

o

total_net_revenue/total_sold/100.0 

o

total_net_revenue.to_f/total_sold/100 

Estos tres métodos dan una cantidad cada vez mayor de precisión, si lo desea. Recuerde que el "precio medio" es "Precio medio/venta Eso es dinero-por-artículo así que usted tendrá que hacer la división en ese orden específico

12

Primera:.. Usted está dividiendo el camino equivocado

. 100 elementos/$ 150 = 0.667 artículos por dólar

vs

$ 150/100 items = $ 1.50 por artículo

Segundo: al igual que otros la En los idiomas, debes forzar que uno de los números de la ecuación sea un decimal para que el resultado también se emita como uno. Como sus ingresos son un número entero, eso significaba que los tres valores eran números enteros, lo que significa que obtuvo un número entero como resultado. Para obtener un decimal, eche uno de ellos como un número de coma flotante.

En otras palabras, para obtener lo que necesita, hacer esto:

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

Es necesario para convertir sus valores que están en centavos enteros (enteros) a los flotadores (números con decimales), de modo que la matemáticas da como resultado un número flotante en lugar de un número entero.

Así que, en general, funciona así:

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

lo que está haciendo que se llama división entera, lo que descarta cualquier resto, ya que no sería expresar en números enteros, por ejemplo:

1/3 # == 0 

Como han mencionado otros encuestados, puede forzar una división de coma flotante. Debe forzar que el primer argumento sea un flotante (1 aquí) llamando a .to_f. El segundo argumento será automáticamente coaccionado a un flotador, es decir .:

1.to_f/3 # ~ 0.3333... 

Tenga en cuenta que una vez que se muda a números de punto flotante, el resultado, en términos generales, ya no es exacta. Es por eso que puse ~ 0.333.

Los detalles precisos están más involucrados. En la aritmética de coma flotante binaria, que es común en los microprocesadores actuales, los poderes de 2 siguen siendo exactos, creo.Pero el entero 3, por ejemplo, ya no está representado exactamente, sino solo dentro de la precisión de la representación de punto flotante (típicamente 1E-16 o más o menos para la precisión "doble").

En pocas palabras, aquí hay una regla general: si se trata de valores monetarios, donde la precisión importa (¿alguna vez se notó una discrepancia de 1 centavo en una factura telefónica?) No se almacenan los resultados computados no almacena valores en puntos flotantes En su lugar, utilice tipos de datos enteros o decimales (que almacenan cadenas internamente). Calcule los resultados de coma flotante solo para la visualización y la demanda, si es posible. Evite agregar valores grandes y pequeños juntos una vez que estén flotando, y evite los cálculos encadenados en flotadores. Vuelva a trabajar su álgebra para evitar divisiones hasta el final. Ruby también admite un tipo de datos Rational que representa fracciones exactamente y puede ser útil.

Estos problemas caen bajo la ciencia de la "propagación de error de coma flotante", que es donde puede buscar más información si lo necesita.

+0

Gracias por la explicación detallada. Normalmente almaceno los valores monetarios como centavos y los calculo basados ​​en centavos para evitar decimales porque necesito almacenar esos valores de vez en cuando. – Slick23

+0

Eso es lo que encontré que funciona generalmente también mejor. También hay algunas gemas, p. la gema del dinero, eso agrega cierta facilidad a ese enfoque. –

0

Incluso si uno de los valores es Float, el resultado será Float.

Además, si usa Rails y sus almacenes de datos en un modelo específico, ActiveRecord tiene métodos especiales y no necesita calcular usted mismo.

Model.average("field_with_data") 

También disponible los métodos mínimo, máximo, recuento, suma.