logo

Avrundning av fel i Java

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:

Java
public 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:

java
public 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:

Java
public 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.09999999999999998

Hur å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:
Java
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.1

Hä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.

Java
public 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