Att komprimera många oändliga reella tal till ett ändligt antal bitar kräver en ungefärlig representation. De flesta program lagrar resultatet av heltalsberäkningar vid max 32 eller 64 bitar. Givet ett fast antal bitar kommer de flesta beräkningar med reella tal att producera kvantiteter som inte kan representeras exakt med så många bitar. Därför måste resultatet av en flyttalsberäkning ofta avrundas för att passa tillbaka i dess finita representation. Detta avrundningsfel är ett karakteristiskt särdrag för flyttalsberäkning. När vi hanterar beräkningar i flyttal (speciellt om beräkningar är i termer av pengar) måste vi därför ta hand om avrundningsfel i ett programmeringsspråk. Låt oss se ett exempel:
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); } }
gör ett sh-skript körbart
Produktion:
x = 0.7999999999999999
y = 0.8
false
Här är svaret inte vad vi förväntade oss för att vara avrundningen gjord av java-kompilatorn.
Orsak bakom avrundningsfel
Flytande och dubbeldatatyper implementerar IEEE flyttal 754-specifikation. Detta betyder att siffror representeras i en form som:
SIGN FRACTION * 2 ^ EXP 0,15625 = (0,00101)2som i flyttalsformat representeras som: 1,01 * 2^-3
Alla bråk kan inte representeras exakt som en bråkdel av en potens av två. Som ett enkelt exempel 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 och kan således inte lagras i en flyttalsvariabel.
Ett annat exempel:
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); } }
Produktion:
x = 0.7999999999999999
y = 0.8
false
Ett annat exempel:
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); } }
Produktion:
a = 0.09999999999999998Hur åtgärdar man avrundningsfel?
- Runda resultatet: Funktionen Round() kan användas för att minimera eventuella effekter av aritmetisk lagring med flyttal. Användaren kan avrunda siffror till det antal decimaler som krävs för beräkningen. Till exempel när du arbetar med valuta skulle du troligen avrunda till 2 decimaler.
- Algoritmer och funktioner: Använd numeriskt stabila algoritmer eller designa dina egna funktioner för att hantera sådana fall. Du kan trunkera/runda siffror som du inte är säker på att de är korrekta för (du kan också beräkna numerisk precision för operationer)
- BigDecimal Class: Du kan använda java.math.BigDecimal klass som är utformad för att ge oss noggrannhet, särskilt vid stora bråktal. Följande program visar hur felet kan tas bort:
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); } }
Produktion:
0.1Här a = a.setScale(1 RoundingMode.HALF_UP);
Omgångar atill 1 decimal med avrundningsläget HALF_UP. Så att använda BigDecimal ger mer exakt kontroll över aritmetiken och avrundningsoperationerna, vilket kan vara särskilt användbart för finansiella beräkningar eller andra fall där precision är avgörande.
Viktig anmärkning:
Math.round avrundar värdet till närmaste heltal. Eftersom 0,10 är närmare 0 än 1 avrundas det till 0. Efter avrundningen och divisionen med 1,0 blir resultatet 0,0. Så du kan märka skillnaden mellan utdata med BigDecimal class och Maths.round-funktionen.
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); } }
Produktion:
0.0