Convierte doble a BigDecimal y establece la precisión BigDecimal

En Java, quiero tomar un doble valor y convertirlo en un BigDecimal e imprimir su valor String con una 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 estoy haciendo la conversión de BigDecimal es que a veces el valor doble contiene muchos lugares decimales ( -.000012 Ej., -.000012 ) y al convertir el doble en una cadena producirá notación científica -1.2E-4 . Quiero almacenar el valor de cadena en notación no científica.

Quiero que BigDecimal siempre tenga dos unidades de precisión como esta: “47.48”. ¿Puede BigDecimal restringir la precisión en la conversión a cadena?

Imprime 47.48000 si usa 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 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.

Según javadoc, el comportamiento del constructor BigDecimal (doble val) puede ser inesperado si no se tiene en cuenta esta limitación:

Los resultados de este constructor pueden ser algo impredecibles. Uno podría suponer que escribir un nuevo 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, para el caso, como una fracción binaria de cualquier longitud finita). Por lo tanto, el valor que se transfiere al constructor no es exactamente igual a 0.1, a pesar de las apariencias.

Entonces en tu caso, en lugar de usar

 double val = 77.48; new BigDecimal(val); 

utilizar

 BigDecimal.valueOf(val); 

El valor devuelto por BigDecimal.valueOf es igual al resultado de la invocación de Double.toString(double) .

Desea probar String.format("%f", d) , que imprimirá su doble en notación decimal. No use BigDecimal en absoluto.

En cuanto al problema de la precisión: Primero está almacenando 47.48 en la double c , y luego BigDecimal un nuevo BigDecimal partir de ese double . La pérdida de precisión está en la asignación a c . Podrías hacerlo

 BigDecimal b = new BigDecimal("47.48") 

para evitar perder cualquier precisión.

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 doble, imprime exactamente los dígitos que x es el double más cercano al valor que imprimió .

El punto es que no hay un double como 47.48 exactamente. Los dobles almacenan valores como fracciones binarias , no como decimales, por lo que no pueden almacenar valores decimales exactos. (¡Para eso es BigDecimal !)

La syntax String.format nos ayuda a convertir dobles y BigDecimales en cadenas de cualquier precisión.

Este código de 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 

Por qué no :

 b = b.setScale(2, RoundingMode.HALF_UP); 
 BigDecimal b = new BigDecimal(c).setScale(2,BigDecimal.ROUND_HALF_UP); 

En Java 9, lo siguiente está en desuso:

BigDecimal.valueOf(d). setScale (2, BigDecimal. ROUND_HALF_UP );

en su lugar use:

 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