Doble contra BigDecimal?

Tengo que calcular algunas variables de coma flotante y mi colega me sugiere usar BigDecimal lugar de double ya que será más preciso. Pero quiero saber de qué se trata y cómo sacarle el máximo provecho a BigDecimal .

Un BigDecimal es una forma exacta de representar números. Un Double tiene una cierta precisión. Trabajar con dobles de varias magnitudes (digamos d1=1000.0 y d2=0.001 ) podría ocasionar que el 0.001 se elimine por completo al sumr ya que la diferencia en magnitud es muy grande. Con BigDecimal esto no sucedería.

La desventaja de BigDecimal es que es más lento, y es un poco más difícil progtwigr algoritmos de esa manera (debido a que + - * y / no están sobrecargados).

Si está tratando con dinero, o la precisión es imprescindible, use BigDecimal . De lo contrario, los Doubles tienden a ser lo suficientemente buenos.

Recomiendo leer el javadoc de BigDecimal ya que explican las cosas mejor que yo aquí 🙂

Mi inglés no es bueno, así que simplemente voy a escribir un ejemplo simple aquí.

  double a = 0.02; double b = 0.03; double c = b - a; System.out.println(c); BigDecimal _a = new BigDecimal("0.02"); BigDecimal _b = new BigDecimal("0.03"); BigDecimal _c = _b.subtract(_a); System.out.println(_c); 

Salida del progtwig:

 0.009999999999999998 0.01 

Alguien todavía quiere usar el doble? 😉

Hay dos diferencias principales entre el doble:

  • Precisión arbitraria, de manera similar a BigInteger, pueden contener números de precisión y tamaño arbitrarios
  • Base 10 en lugar de Base 2, un BigDecimal es una escala n * 10 ^ donde n es un entero arbitrario grande y la escala se puede considerar como el número de dígitos para mover el punto decimal hacia la izquierda o hacia la derecha

La razón por la que debe usar BigDecimal para cálculos monetarios no es que pueda representar ningún número, sino que puede representar todos los números que pueden representarse en noción decimal y que incluyen prácticamente todos los números en el mundo monetario (nunca transfiere 1/3 $ a alguien).

Si quieres anotar un valor como 1/7 como valor decimal obtienes

 1/7 = 0.142857142857142857142857142857142857142857... 

con una secuencia infinita de 142857. Pero dado que solo puede escribir un número finito de dígitos, inevitablemente introducirá un error de redondeo (o truncamiento).

Desafortunadamente, los números como 1/10 o 1/100 expresados ​​como números binarios con una parte fraccionaria también tienen un número infinito de binarios decimales .

 1/10 = binary 0.000110011001100110... 

Los dobles almacenan valores como números binarios y, por lo tanto, pueden introducir un error únicamente al convertir un número decimal en un número binario, sin siquiera hacer ninguna aritmética.

Los números decimales (como BigDecimal ), por otro lado, almacenan cada dígito decimal como está. Esto significa que un tipo decimal no es más preciso que un punto flotante binario o un tipo de punto fijo en un sentido general (por ejemplo, no puede almacenar 1/7 sin pérdida de precisión), pero es más preciso para números dados con un número finito de dígitos decimales, como suele ser el caso para los cálculos de dinero.

BigDecimal de Java tiene la ventaja adicional de que puede tener un número arbitrario (pero finito) de dígitos en ambos lados del punto decimal, limitado solo por la memoria disponible.

BigDecimal es la biblioteca numérica de precisión arbitraria de Oracle. BigDecimal es parte del lenguaje Java y es útil para una variedad de aplicaciones que van desde la financiera a la científica (que es donde tipo de am).

No hay nada de malo en usar dobles para ciertos cálculos. Supongamos, sin embargo, que desea calcular Math.Pi * Math.Pi / 6, es decir, el valor de la Función Zeta de Riemann para un argumento real de dos (un proyecto en el que estoy trabajando actualmente). La división de coma flotante te presenta un doloroso problema de error de redondeo.

BigDecimal, por otro lado, incluye muchas opciones para calcular expresiones con precisión arbitraria. Los métodos para agregar, multiplicar y dividir como se describe en la documentación de Oracle a continuación “toman el lugar” de +, * y / en BigDecimal Java World:

http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

El método compareTo es especialmente útil en while y for loops.

Tenga cuidado, sin embargo, en su uso de constructores para BigDecimal. El constructor de cadenas es muy útil en muchos casos. Por ejemplo, el código

BigDecimal onethird = new BigDecimal (“0.33333333333”);

utiliza una representación de cadena de 1/3 para representar ese número infinitamente repetitivo con un grado específico de precisión. El error de redondeo es más probable en algún lugar tan profundo dentro de la JVM que los errores de redondeo no perturben la mayoría de sus cálculos prácticos. Sin embargo, debido a mi experiencia personal, he visto rodar por ahí. El método setScale es importante en estos aspectos, como se puede ver en la documentación de Oracle.

 package j2ee.java.math; /** * Generated from IDL definition of "valuetype "BigDecimal"" * TomORB IDL compiler v1.0 */ public abstract class BigDecimal extends j2ee.java.lang.Number implements org.omg.CORBA.portable.StreamableValue, j2ee.java.lang.Comparable { private String[] _truncatable_ids = {"RMI:java.math.BigDecimal:11F6D308F5398BBD:54C71557F981284F"}; protected int scale_; protected j2ee.java.math.BigInteger intVal; /* constants */ int ROUND_UP = 0; int ROUND_DOWN = 1; int ROUND_CEILING = 2; int ROUND_FLOOR = 3; int ROUND_HALF_UP = 4; int ROUND_HALF_DOWN = 5; int ROUND_HALF_EVEN = 6; int ROUND_UNNECESSARY = 7; public abstract int _hashCode(); public abstract int scale(); public abstract int signum(); public abstract boolean _equals(org.omg.CORBA.Any arg0); public abstract java.lang.String _toString(); public abstract j2ee.java.math.BigDecimal abs(); public abstract j2ee.java.math.BigDecimal negate(); public abstract j2ee.java.math.BigDecimal movePointLeft(int arg0); public abstract j2ee.java.math.BigDecimal movePointRight(int arg0); public abstract j2ee.java.math.BigDecimal setScale(int arg0); public abstract j2ee.java.math.BigDecimal setScale(int arg0, int arg1); public abstract j2ee.java.math.BigDecimal valueOf(long arg0); public abstract j2ee.java.math.BigDecimal valueOf(long arg0, int arg1); public abstract int compareTo(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigInteger toBigInteger(); public abstract j2ee.java.math.BigInteger unscaledValue(); public abstract j2ee.javax.rmi.CORBA.ClassDesc classU0024(java.lang.String arg0); public abstract j2ee.java.math.BigDecimal add(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal max(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal min(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal multiply(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal subtract(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1); public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1, int arg2); public void _write (org.omg.CORBA.portable.OutputStream os) { super._write( os ); os.write_long(scale_); ((org.omg.CORBA_2_3.portable.OutputStream)os).write_value( new java.lang.String("intVal") ); } public void _read (final org.omg.CORBA.portable.InputStream os) { super._read( os ); scale_=os.read_long(); intVal=(j2ee.java.math.BigInteger)((org.omg.CORBA_2_3.portable.InputStream)os).read_value ( "RMI:java.math.BigInteger:E2F79B6E7A470003:8CFC9F1FA93BFB1D".toString() ); } public String[] _truncatable_ids() { return _truncatable_ids; } public org.omg.CORBA.TypeCode _type() { return j2ee.java.math.BigDecimalHelper.type(); } } 

Los tipos numéricos primitivos son útiles para almacenar valores individuales en la memoria. Pero cuando se trata de calcular utilizando tipos doble y flotante, hay un problema con el redondeo. Esto sucede porque la representación de la memoria no se corresponde exactamente con el valor. Por ejemplo, se supone que un valor doble toma 64 bits pero Java no usa todos los 64 bits. Solo almacena lo que cree que son las partes importantes del número. Por lo tanto, puede llegar a los valores incorrectos al agregar valores del tipo flotante o doble. Puede ser este video https://youtu.be/EXxUSz9x7BM le explicará más