Botón para abrir el Menú Botón para cerrar el Menú
Logo da empresa Alura
Iniciar Sesión Nuestros Planes
Formaciones Conoce a Luri
  • Programación _
  • Front End _
  • Data Science _
  • DevOps _
  • Innovación y Gestión _
Artículos de Tecnología > Programación

Redondeo y números gigantes: de double a BigDecimal

Alura
Paulo Silveira
Paulo Silveira
27/04/2021

Compartir

Es fácil encontrar las limitaciones del doublé en Java y la mayoría de los otros lenguajes: cuando vamos a trabajar con dinero notamos que las cuentas no están saliendo exactamente como esperábamos:

double d1 = 0.1;
double d2 = 0.2;
System.out.println (d1 + d2);

El resultado es un extraño 0.30000000000000004, lo que puede causar serios problemas según el uso y el redondeo aplicado posteriormente a ese número. El problema es que un número con 0.1 no se puede representar en binario de una manera finita: se convierte en un diezmo (en el binario se vería como 0.110011001100…) diferente del número 0.25, que se puede representar perfectamente (en el binario 0.01). La representación es un poco más complicada que eso, JVM sigue el estándar IEEE 754 para trabajar con números de punto flotante.

https://i2.wp.com/blog.caelum.com.br/wp-content/uploads/2010/07/numbers.jpeg?resize=270%2C300&ssl=1

¿Cómo conseguir el esperado 0.3? La sugerencia es siempre utilizar el BigDecimal. BigDecimal es una clase que trabaja con números de punto flotante de precisión arbitraria: puede elegir cuánta precisión desea usar. Por estándar, utilizará lo que sea necesario y, a diferencia del double, puede almacenar números como 0.1, ya que guardará esto como 1 x 10ˆ-1 (es decir, usando la base decimal en lugar de binaria, evitando el diezmo).

// no use este constructor:
BigDecimal big1 = new BigDecimal(0.1);
BigDecimal big2 = new BigDecimal(0.2); 
System.out.println(big1.add(big2));

El resultado es una nueva sorpresa, una increíble 0.300000000000000016653345369377.... Lo que hicimos mal ahora fue intentar sumar 0.1 y 0.2 siendo que estos dos números ya estaban almacenados en la memoria como double, y, cuando se pasaron al constructor del BigDecimal, fueron transportados con imprecisión. El propio javadoc de este constructor dice que "Los resultados de este constructor pueden ser algo impredecibles“. De hecho el resultado es bastante predecible según sus reglas, pero no es lo que nos gustaría.

¿Cómo solucionar? Utiliza siempre el constructor que trabaja con Strings, entonces el BigDecimal hará internamente el parsing de estos números sin que se almacenen en un double, evitando problemas de precisión:

// ¡atencion! usando String en el construtor:
BigDecimal big1 = new BigDecimal("0.1");
BigDecimal big2 = new BigDecimal("0.2"); 
System.out.println(big1.add(big2));

Por fin obteniendo el resultado esperado. También hay observaciones importantes sobre el BigDecimal: por estándar no hará ningún tipo de redondeo, lo que lo obliga a registrar java.lang.ArithmeticException en el caso de un décimo decimal (intenta dividir 1/3, por ejemplo). En estos casos, es necesario limitar el número de bits que se utilizarán o elegir el modo de redondeo:

BigDecimal big1 = new BigDecimal("1");
BigDecimal big2 = new BigDecimal("3");
System.out.println(big1.divide(big2, 3, RoundingMode.UP));

Resultando en 0.334. También vale la pena recordar la inmutabilidad de la clase BigDecimal, que tiene varias ventajas, pero se debe usar con cuidado cuando se realizarán varias operaciones en el mismo número dentro de un bucle, ya que varios BigDecimals se instanciarán durante la operación, lo que puede resultar en el mismo problema de desempeño del uso de la concatenación de Strings. Donizetti recordó que este tema se discute ampliamente en el numeral 48 de Effective Java.

En JavaScript tendremos el mismo problema si necesitas hacer cuentas en el lado del cliente, y luego podemos usar BigDecimalJS, que funciona de forma similar a Java.

Rafael Ferreira nos recuerda que podemos ir más allá, y como el dinero es algo que pertenece a nuestro dominio y lógica de negocios, creamos una clase Money para encapsular todo este comportamiento y prevenir que RoundingMode, MathContext y escalas se esparzan por todo tu código.

¿Te interesa este tipo de contenido? Les invitamos a conocer la página de Alura, donde encontrarás diversos cursos de Java.

Paulo Silveira
Paulo Silveira

Paulo Silveira é CEO e cofundador da Alura. Bacharel e mestre em Ciência da Computação pela USP, teve sua carreira de formação em PHP, Java e nas maratonas de programação. Criou o Guj.com.br, o podcast do Hipsters.tech e o Like a Boss.

Artículo Anterior
Cómo no aprender Java y Orientación a Objetos: getters y setters
Siguiente Artículo
Reciba notificaciones de la api de Servlet a través de Listeners

Ver otros artículos sobre Programación

Navegación

  • Planes
  • Instructores
  • Blog
  • Política de privacidad
  • Términos de uso
  • Sobre nosotros
  • Preguntas frecuentes

¡CONTÁCTANOS!

  • ¡Quiero entrar en contacto!

Blog

  • Programación
  • Data Science
  • Front End
  • Innovación y Gestión
  • DevOps

AOVS Sistemas de Informática S.A CNPJ 05.555.382/0001-33

SÍGUENOS EN NUESTRAS REDES SOCIALES

YouTube Facebook Instagram Linkedin Whatsapp Spotify

NOVEDADES Y LANZAMIENTOS

Aliados

  • Programa de aceleração Scale-Up Endeavor
  • En Alura somos unas de las Scale-Ups seleccionadas por Endeavor, programa de aceleración de las empresas que más crecen en el país.
  • Growth Academy 2021 do Google For Startups
  • Fuimos unas de las 7 startups seleccionadas por Google For Startups en participar del programa Growth Academy en 2021
Alura

Powered by

Caelum

AOVS Sistemas de Informática S.A CNPJ 05.555.382/0001-33

SÍGUENOS EN NUESTRAS REDES SOCIALES

YouTube Facebook Instagram Linkedin Whatsapp Spotify

Cursos

Cursos de Programación
Lógica de Programación | Java
Cursos de Front End
HTML y CSS | JavaScript | React
Cursos de Data Science
Data Science | Machine Learning | Excel | Base de Datos | Data Visualization | Estadística
Cursos de DevOps
Docker | Linux
Cursos de Innovación y Gestión
Transformación Ágil | Marketing Analytics

Alura

  • Educação em Tecnologia

    • logo fiap FIAP
    • logo casa do codigo Casa do Código
    • logo pm3 PM3 - Cursos de Produto
  • Mais Alura

    • logo alura start START BY Alura
    • logo alura lingua Alura Língua
    • logo alura para empresas Alura Para Empresas
    • logo alura latam Alura LATAM
  • Comunidade

    • logo tech guide Tech Guide
    • logo 7 days of code 7 days of code
    • logo Hipsters ponto Jobs Hipsters ponto Jobs
  • Podcasts

    • logo Hipster Network Hipster Network
    • logo Hipsters ponto Tech Hipsters ponto Tech
    • logo Dev sem fronteiras Dev sem Fronteiras
    • logo Like a Boss Like a Boss
    • logo IA Sob Controle IA Sob Controle
    • logo Mesa de Produto Mesa de Produto
    • logo Decode Decode
    • logo FIAPCast FIAPCast