type rails money monetize ruby-on-rails ruby math floating-point bigdecimal

ruby-on-rails - rails - monetize gem



Verificación de cordura de Ruby BigDecimal(punto flotante newb) (1)

Hay dos fallas comunes cuando se trabaja con aritmética de punto flotante.

El primer problema es que los puntos flotantes de Ruby tienen una precisión fija. En la práctica, esto será 1) sin problemas para usted o 2) desastroso, o 3) algo intermedio. Considera lo siguiente:

# float 1.0e+25 - 9999999999999999900000000.0 #=> 0.0 # bigdecimal BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0") #=> 100000000

Una diferencia de precisión de 100 millones! Bastante serio, ¿verdad?

Excepto que el error de precisión es solo alrededor de 0.000000000000001% del número original. Realmente depende de usted decidir si esto es un problema o no. Pero el problema se elimina utilizando BigDecimal porque tiene una precisión arbitraria. Tu único límite es la memoria disponible para Ruby.

El segundo problema es que los puntos flotantes no pueden expresar todas las fracciones con precisión. En particular, tienen problemas con las fracciones decimales , porque las flotaciones en Ruby (y en la mayoría de los otros idiomas) son puntos flotantes binarios . Por ejemplo, la fracción decimal 0.2 es una fracción binaria que se repite eternamente ( 0.001100110011... ). Esto nunca se puede almacenar con precisión en un punto flotante binario, sin importar cuál sea la precisión.

Esto puede hacer una gran diferencia cuando redondeas números. Considerar:

# float (0.29 * 50).round #=> 14 # not correct # bigdecimal (BigDecimal("0.29") * 50).round #=> 15 # correct

Un BigDecimal puede describir fracciones decimales con precisión. Sin embargo, hay fracciones que tampoco se pueden describir con precisión con una fracción decimal. Por ejemplo, 1/9 es una fracción decimal que se repite eternamente ( 0.1111111111111... ).

De nuevo, esto te morderá cuando redondees un número. Considerar:

# bigdecimal (BigDecimal("1") / 9 * 9 / 2).round #=> 0 # not correct

En este caso, el uso de puntos flotantes decimales todavía dará un error de redondeo.

Algunas conclusiones:

  • Los flotadores decimales son impresionantes si haces cálculos con fracciones decimales (dinero, por ejemplo).
  • El BigDecimal de Ruby también funciona bien si necesita puntos flotantes de precisión arbitrarios, y realmente no le importa si son puntos flotantes decimales o binarios.
  • Si trabaja con datos (científicos), normalmente se trata de números de precisión fijos; Los flotadores incorporados de Ruby probablemente serán suficientes.
  • Nunca se puede esperar que la aritmética con cualquier tipo de punto flotante sea precisa en todas las situaciones.

¿Tengo razón al entender que con Ruby BigDecimal tipos (incluso con precisión variable y longitudes de escala) deben calcularse con precisión o debo anticipar las travesuras de punto flotante?

Todos mis valores dentro de una aplicación de Rails son de tipo BigDecimal y estoy viendo algunos errores (tienen diferentes longitudes decimales), esperando que sean solo mis métodos y no mis tipos de objetos.