Estoy haciendo una aplicación de conversión de moneda en Java. Algunos otros StackOverflowians impresionantes me dio consejos para leer sobre BigDecimal con el fin de reemplazar el doble de solucionar los problemas de precisión.
Tengo un sistema de dos método; donde se convierte a la moneda de partida en USD, a continuación, convierte el valor en dólares de la moneda de destino.
Tenga en cuenta, mis tasas de conversión se almacenan como esto:
// Conversion Rates - START (as of October 30, 2018 @ 3:19 AM)
// Rates obtained from exchange-rates.org
//Convert to United States Dollar rates
private final BigDecimal CAD_TO_USD = new BigDecimal(0.76135);
private final BigDecimal EUR_TO_USD = new BigDecimal(1.1345);
private final BigDecimal YEN_TO_USD = new BigDecimal(0.008853);
// Conversion Rates - END
Después reemplacé mis dobles con sus respectivos BigDecimals - decidí probarlo y ver cómo funciona todo.
Mi clase probador ejecuta el método siguiente para iniciar el proceso de conversión.
public BigDecimal convert()
{
BigDecimal value;
value = convertToUSD(); //Converts the current currency into USD
value = convertFromUSD(value); //Converts the previous USD currency value into the destination currency
return value;
}
Cuando entro en mi variables de la muestra (que se están convirtiendo 2,78 yenes a dólares canadienses), di un paso a través del proceso y encontré que todo funciona hasta que regrese un valor.
A partir del método anteriormente mencionado, convertToUSD()
se ejecuta y se codifica como sigue
private BigDecimal convertToUSD()
{
switch (fromCurrency)
{
case "USD":
return fromQuantity.multiply(new BigDecimal(1));
case "CAD":
return fromQuantity.multiply(CAD_TO_USD);
case "EUR":
return fromQuantity.multiply(EUR_TO_USD);
case "YEN":
return fromQuantity.multiply(YEN_TO_USD);
}
return new BigDecimal(0);
}
Todos los valores se transmiten en forma correcta, los pasos a través hasta el tamaño adecuado ( "YEN"), y el panel de muestra variables que el "fromQuantity" BigDecimal tiene un valor de 278 intCompact (que tiene sentido para mí)
Tan pronto como los retornos de punto de interrupción volver al método "convertir", se pone todo en mal estado. En lugar de devolver el 2.78 * 0.008853 = 0.0246
, devuelve -9223372036854775808
.
Esto hace que todos los otros cálculos para producir y error.
Soy nuevo en el uso BigDecimal, así que puede estar cometiendo un error obvio completa; pero estoy feliz de aprender, así que buscó el consejo de ustedes :)
Cualquier ayuda se aprecia.
tl; dr
Uso String
, no double
literales.
new BigDecimal( "2.78" ) // Pass "2.78" not 2.78
.multiply(
new BigDecimal( "0.008853" ) // Pass "0.008853" not 0.008853
)
.toString()
0.02461134
No pase tipos de coma flotante
El punto de la BigDecimal
clase es para evitar las imprecisiones inherentes que se encuentran en coma flotante tecnología. Tipos de coma flotante como float
/ Float
y double
/ Double
comercio de distancia precisión para la velocidad de ejecución. Por el contrario, BigDecimal
es lento pero preciso.
Tu codigo:
new BigDecimal( 0.76135 )
new BigDecimal( 1.1345 )
new BigDecimal( 0.008853 )
... está pasando un double
literal primitivo. Durante la compilación, el texto que ha escrito 0.76135
se analiza como un número, específicamente como double
(un valor de coma flotante de 64 bits). En ese momento usted ha introducido imprecisiones inherentes a este tipo. En otras palabras, el double
producir a partir de 0.76135
ya no puede ser exactamente 0.76135
.
Vamos a volcar sus BigDecimal
casos inmediatamente después de creación de instancias.
System.out.println( new BigDecimal( 0.76135 ) ); // Passing a `double` primitive.
System.out.println( new BigDecimal( 1.1345 ) );
System.out.println( new BigDecimal( 0.008853 ) );
0,7613499999999999712230192017159424722194671630859375
1,13450000000000006394884621840901672840118408203125
0,0088529999999999997584154698415659368038177490234375
Así, mediante la creación de double
valores numéricos, se invocó la tecnología de punto flotante, y se presentó inexactitudes.
Use cuerdas
¿La solución? Trabajar con cadenas, evitando el double
tipo totalmente.
Ponga un poco de comillas dobles alrededor de esas entradas , y voila .
System.out.println( new BigDecimal( "0.76135" ) ); // Passing a `String` object.
System.out.println( new BigDecimal( "1.1345" ) );
System.out.println( new BigDecimal( "0.008853" ) );
0.76135
1.1345
0.008853
Ejemplo
Que esperaba 2.78 * 0.008853 = 0.0246
. Vamos a intentarlo.
BigDecimal x = new BigDecimal( "2.78" );
BigDecimal y = new BigDecimal( "0.008853" );
BigDecimal z = x.multiply( y );
System.out.println( x + " * " + y + " = " + z );
2,78 * 0,008853 = 0,02461134
A continuación, debe estudiar hasta en el redondeo y truncamiento con BigDecimal
. Ya cubiertas muchas veces en desbordamiento de pila.