Compactar molts nombres reals infinits en un nombre finit de bits requereix una representació aproximada. La majoria dels programes emmagatzemen el resultat dels càlculs d'enters a un màxim de 32 o 64 bits. Donat qualsevol nombre fix de bits, la majoria dels càlculs amb nombres reals produiran quantitats que no es poden representar exactament amb tants bits. Per tant, el resultat d'un càlcul de coma flotant s'ha d'arrodonir sovint per tornar a ajustar-se a la seva representació finita. Aquest error d'arrodoniment és un tret característic del càlcul de coma flotant. Per tant, mentre gestionem els càlculs en nombres de coma flotant (especialment si els càlculs són en termes de diners) hem de tenir cura dels errors d'arrodoniment en un llenguatge de programació. Vegem un exemple:
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); } }
css per negreta
Sortida:
x = 0.7999999999999999
y = 0.8
false
Aquí la resposta no és la que esperàvem perquè és l'arrodoniment fet pel compilador Java.
Motiu darrere de l'error d'arrodoniment
Els tipus de dades Float i Double implementen l'especificació 754 de coma flotant IEEE. Això vol dir que els nombres es representen en una forma com:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2que en format de coma flotant es representa com: 1,01 * 2^-3
No totes les fraccions es poden representar exactament com una fracció d'una potència de dos. Com a exemple senzill 0,1 = (0,000110011001100110011001100110011001100110011001100110011001...)2 i per tant no es pot emmagatzemar dins d'una variable de coma flotant.
Un altre exemple:
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); } }
Sortida:
x = 0.7999999999999999
y = 0.8
false
Un altre exemple:
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); } }
Sortida:
a = 0.09999999999999998Com rectificar els errors d'arrodoniment?
- Arrodoneix el resultat: La funció Round() es pot utilitzar per minimitzar els efectes de la imprecisió de l'emmagatzematge aritmètic de coma flotant. L'usuari pot arrodonir els nombres al nombre de decimals que requereix el càlcul. Per exemple, mentre treballeu amb moneda, probablement arrodoniu a 2 decimals.
- Algorismes i funcions: Utilitzeu algorismes numèricament estables o dissenyeu les vostres pròpies funcions per gestionar aquests casos. Podeu truncar/arrodonir els dígits dels quals no esteu segur que siguin correctes (també podeu calcular la precisió numèrica de les operacions)
- Classe BigDecimal: Podeu utilitzar el java.math.BigDecimal classe que està dissenyada per donar-nos precisió, especialment en cas de nombres fraccionaris grans. El programa següent mostra com es pot eliminar l'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); } }
Sortida:
0.1Aquí a = a.setScale(1 RoundingMode.HALF_UP);
Rondes aa 1 decimal mitjançant el mode d'arrodoniment HALF_UP. Per tant, utilitzar BigDecimal proporciona un control més precís sobre les operacions aritmètiques i d'arrodoniment que poden ser especialment útils per als càlculs financers o altres casos en què la precisió és crucial.
Nota important:
Math.round arrodoneix el valor al nombre enter més proper. Com que 0,10 és més proper a 0 que a 1, s'arrodoneix a 0. Després de l'arrodoniment i la divisió per 1,0, el resultat és 0,0. Així que podeu notar la diferència entre les sortides amb la classe BigDecimal i la funció 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); } }
Sortida:
0.0