big double java
Doble vs BigDecimal? (8)
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 lo financiero a lo científico (ahí es donde estoy).
No hay nada de malo en usar dobles para ciertos cálculos. Sin embargo, suponga 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 punto flotante le presenta un problema doloroso de error de redondeo.
BigDecimal, por otro lado, incluye muchas opciones para calcular expresiones con precisión arbitraria. Los métodos de agregar, multiplicar y dividir, tal como se describen en la documentación de Oracle a continuación "ocupe 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 para bucles.
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 que se repite infinitamente con un grado de precisión específico. El error de redondeo es más probable en algún lugar tan profundo dentro de la JVM que los errores de redondeo no perturbarán la mayoría de sus cálculos prácticos. Sin embargo, desde mi experiencia personal, he visto un aumento gradual. El método setScale es importante en estos aspectos, como puede verse en la documentación de Oracle.
Tengo que calcular algunas variables de punto flotante y mi colega me sugiere que use BigDecimal
lugar de double
ya que será más preciso. ¿Pero quiero saber qué es y cómo aprovechar al máximo BigDecimal
?
Hay dos diferencias principales de doble:
- Precisión arbitraria, al igual que BigInteger, pueden contener número 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 con signo grande arbitrario 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 los cálculos monetarios no es que pueda representar cualquier 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).
Los tipos numéricos primitivos son útiles para almacenar valores individuales en la memoria. Pero cuando se trata de cálculos con tipos dobles y flotantes, hay un problema con el redondeo. Ocurre 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 los 64 bits. Solo almacena lo que cree que son las partes importantes del número. Por lo tanto, puede llegar a los valores equivocados al agregar valores de tipo flotante o doble. Puede ser este video https://youtu.be/EXxUSz9x7BM explicará más
Mi inglés no es bueno, así que 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 programa:
0.009999999999999998
0.01
¿Alguien todavía quiere usar doble? ;)
Si desea escribir un valor como 1/7 como valor decimal, obtendrá
1/7 = 0.142857142857142857142857142857142857142857...
con una secuencia infinita de 142857. Pero como solo puedes escribir un número finito de dígitos, inevitablemente introducirás 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 los 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 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 binario flotante o 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 de dígitos arbitrario (pero finito) en ambos lados del punto decimal, limitado solo por la memoria disponible.
Si está tratando con el cálculo, existen leyes sobre cómo debe calcular y qué precisión debe usar. Si fallas eso estarás haciendo algo ilegal. La única razón real es que la representación de bits de los casos decimales no es precisa. Como simplemente dijo Basil, un ejemplo es la mejor explicación. Solo para complementar su ejemplo, esto es lo que sucede:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:/t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:/t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:/t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Salida:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
También tenemos que:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:/t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:/t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:/t 10 / 3 = " + (bd3.divide(bd4)));
}
Nos da la salida:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Pero:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:/t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Tiene la salida:
BigDec: 10 / 3 = 3.3333
Un BigDecimal
es una forma exacta de representar números. Un Double
tiene una cierta precisión. Trabajar con dobles de diferentes magnitudes (por ejemplo, d1=1000.0
y d2=0.001
) podría dar como resultado que se caiga 0.001 en total cuando se suma, ya que la diferencia en la magnitud es tan 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 programar algoritmos de esa manera (debido a que +
-
*
y /
no están sobrecargados).
Si está tratando con dinero, o la precisión es una necesidad, use BigDecimal
. De lo contrario, los Doubles
tienden a ser lo suficientemente buenos.
Recomiendo leer el javadoc de BigDecimal
ya que explican mejor las cosas que aquí :)
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();
}
}