Compactar muchos números reales infinitos en un número finito de bits requiere una representación aproximada. La mayoría de los programas almacenan el resultado de cálculos de números enteros a 32 o 64 bits como máximo. Dado un número fijo de bits, la mayoría de los cálculos con números reales producirán cantidades que no se pueden representar exactamente con tantos bits. Por lo tanto, el resultado de un cálculo de punto flotante a menudo debe redondearse para que se ajuste a su representación finita. Este error de redondeo es un rasgo característico del cálculo de punto flotante. Por lo tanto, al manejar cálculos en números de coma flotante (especialmente si los cálculos se realizan en términos de dinero), debemos ocuparnos de los errores de redondeo en un lenguaje de programación. Veamos un ejemplo:
Javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
cadena a carácter java
Producción:
x = 0.7999999999999999
y = 0.8
false
Aquí la respuesta no es la que esperábamos, ya que el redondeo lo realiza el compilador de Java.
Razón detrás del error de redondeo
Los tipos de datos flotantes y dobles implementan la especificación 754 de punto flotante IEEE. Esto significa que los números se representan de la siguiente forma:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2que en formato de punto flotante se representa como: 1.01 * 2^-3
No todas las fracciones se pueden representar exactamente como una fracción de una potencia de dos. Como ejemplo simple 0.1 = (0.000110011001100110011001100110011001100110011001100110011001…)2 y por lo tanto no se puede almacenar dentro de una variable de punto flotante.
Otro ejemplo:
javapublic class Main { public static void main(String[] args) { double a = 0.7; double b = 0.9; double x = a + 0.1; double y = b - 0.1; System.out.println('x = ' + x); System.out.println('y = ' + y ); System.out.println(x == y); } }
Producción:
x = 0.7999999999999999
y = 0.8
false
Otro ejemplo:
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); // Value of a is expected as 0.1 System.out.println('a = ' + a); } }
Producción:
a = 0.09999999999999998¿Cómo rectificar los errores de redondeo?
- Redondea el resultado: La función Round() se puede utilizar para minimizar cualquier efecto de la inexactitud del almacenamiento aritmético de punto flotante. El usuario puede redondear números al número de decimales requerido por el cálculo. Por ejemplo, cuando trabaje con moneda, probablemente redondeará a 2 decimales.
- Algoritmos y funciones: Utilice algoritmos numéricamente estables o diseñe sus propias funciones para manejar tales casos. Puede truncar/redondear dígitos de los que no esté seguro de que sean correctos (también puede calcular la precisión numérica de las operaciones)
- Clase BigDecimal: Puedes usar el java.math.BigDecimal clase que está diseñada para brindarnos precisión, especialmente en el caso de números fraccionarios grandes. El siguiente programa muestra cómo se puede eliminar el error:
import java.math.BigDecimal; import java.math.RoundingMode; public class Main { public static void main(String args[]) { BigDecimal a = new BigDecimal('1.0'); BigDecimal b = new BigDecimal('0.10'); BigDecimal x = b.multiply(new BigDecimal('9')); a = a.subtract(x); // Rounding to 1 decimal place a = a.setScale(1 RoundingMode.HALF_UP); System.out.println('a = ' + a); } }
Producción:
0.1Aquí a = a.setScale(1 RoundingMode.HALF_UP);
Rondas aa 1 decimal usando el modo de redondeo HALF_UP. Por lo tanto, el uso de BigDecimal proporciona un control más preciso sobre las operaciones aritméticas y de redondeo, lo que puede resultar especialmente útil para cálculos financieros u otros casos en los que la precisión es crucial.
Nota importante:
Math.round redondea el valor al entero más cercano. Como 0,10 está más cerca de 0 que de 1, se redondea a 0. Después del redondeo y la división por 1,0, el resultado es 0,0. Entonces puede notar la diferencia entre las salidas con la clase BigDecimal y la función Maths.round.
Javapublic class Main { public static void main(String args[]) { double a = 1.0; double b = 0.10; double x = 9 * b; a = a - (x); /* We use Math.round() function to round the answer to closest long then we multiply and divide by 1.0 to to set the decimal places to 1 place (this can be done according to the requirements.*/ System.out.println('a = ' + Math.round(a*1.0)/1.0); } }
Producción:
0.0