java - valueof - Convertir el doble a BigDecimal y establecer la precisión BigDecimal
java convertir bigdecimal a double (8)
En Java, quiero tomar un doble valor y convertirlo en un BigDecimal
e imprimir su valor de String con cierta precisión.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
double d=-.00012;
System.out.println(d+""); //This prints -1.2E-4
double c=47.48000;
BigDecimal b = new BigDecimal(c);
System.out.println(b.toString());
//This prints 47.47999999999999687361196265555918216705322265625
}
}
Imprime esta gran cosa:
47.47999999999999687361196265555918216705322265625
y no
47.48
La razón por la que hago la conversión BigDecimal
es que a veces el valor doble contendrá muchos decimales (es decir, -.000012
) y al convertir el doble en una cadena producirá una notación científica -1.2E-4
. Quiero almacenar el valor de String en notación no científica.
Quiero que BigDecimal tenga siempre dos unidades de precisión como esta: "47.48". ¿Puede BigDecimal restringir la precisión en la conversión a cadena?
Desea probar String.format("%f", d)
, que imprimirá su doble en notación decimal. No uses BigDecimal
en absoluto.
Respecto a la cuestión de la precisión: primero está almacenando 47.48
en la double c
, luego haciendo un nuevo BigDecimal
partir de esa double
. La pérdida de precisión está en c
. Podrías hacerlo
BigDecimal b = new BigDecimal("47.48")
Para evitar perder cualquier precisión.
En Java 9 lo siguiente está en desuso:
BigDecimal.valueOf(d).
setScale (2, BigDecimal.
ROUND_HALF_UP );
en su lugar utiliza:
BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_UP);
Ejemplo:
double d = 47.48111;
System.out.println(BigDecimal.valueOf(d)); //Prints: 47.48111
BigDecimal bigDecimal = BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_UP);
System.out.println(bigDecimal); //Prints: 47.48
Imprime 47.48000 si usas otro MathContext :
BigDecimal b = new BigDecimal(d, MathContext.DECIMAL64);
Solo elige el contexto que necesitas.
La razón de tal comportamiento es que la cadena que se imprime es el valor exacto, probablemente no es lo que esperaba, pero ese es el valor real almacenado en la memoria, es solo una limitación de la representación de punto flotante.
De acuerdo con javadoc, el comportamiento del constructor BigDecimal (doble val) puede ser inesperado si no tiene en cuenta esta limitación:
Los resultados de este constructor pueden ser algo impredecibles. Se podría suponer que la escritura new BigDecimal (0,1) en Java crea un BigDecimal que es exactamente igual a 0,1 (un valor sin escala de 1, con una escala de 1), pero en realidad es igual a 0,1000000000000000055511151231257827021181583404541015625. Esto se debe a que 0.1 no se puede representar exactamente como un doble (o, en realidad, como una fracción binaria de cualquier longitud finita). Por lo tanto, el valor que se pasa al constructor no es exactamente igual a 0.1, a pesar de las apariencias.
Así que en tu caso, en lugar de usar
double val = 77.48;
new BigDecimal(val);
utilizar
BigDecimal.valueOf(val);
El valor que devuelve BigDecimal.valueOf es igual al que resulta de la invocación de Double.toString(double)
.
Por qué no :
b = b.setScale(2, RoundingMode.HALF_UP);
Se está imprimiendo el valor real y exacto del double
.
Double.toString()
, que convierte double
s en String
s, no imprime el valor decimal exacto de la entrada; si x
es su valor double, imprime exactamente los dígitos suficientes para que x
sea el double
más cercano al valor que imprimió .
El punto es que no hay exactamente el double
47.48. Los dobles almacenan los valores como fracciones binarias , no como decimales, por lo que no pueden almacenar valores decimales exactos. (¡Para eso es BigDecimal
!)
La sintaxis de String.format nos ayuda a convertir dobles y BigDecimals en cadenas de cualquier precisión.
Este código java:
double dennis = 0.00000008880000d;
System.out.println(dennis);
System.out.println(String.format("%.7f", dennis));
System.out.println(String.format("%.9f", new BigDecimal(dennis)));
System.out.println(String.format("%.19f", new BigDecimal(dennis)));
Huellas dactilares:
8.88E-8
0.0000001
0.000000089
0.0000000888000000000
BigDecimal b = new BigDecimal(c).setScale(2,BigDecimal.ROUND_HALF_UP);