logo

Kļūdu noapaļošana Java

Daudzu bezgalīgu reālu skaitļu sablīvēšanai ierobežotā bitu skaitā ir nepieciešams aptuvens attēlojums. Lielākā daļa programmu saglabā veselu skaitļu aprēķinu rezultātus ar maksimālo lielumu 32 vai 64 biti. Ņemot vērā jebkuru fiksētu bitu skaitu, lielākā daļa aprēķinu ar reāliem skaitļiem radīs lielumus, kurus nevar precīzi attēlot, izmantojot tik daudz bitu. Tāpēc peldošā komata aprēķina rezultāts bieži ir jānoapaļo, lai ietilptu atpakaļ tā galīgajā attēlojumā. Šī noapaļošanas kļūda ir raksturīga peldošā komata skaitļošanas iezīme. Tāpēc, veicot aprēķinus peldošā komata skaitļos (īpaši, ja aprēķini ir naudas izteiksmē), mums ir jārūpējas par noapaļošanas kļūdām programmēšanas valodā. Apskatīsim piemēru:

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);  } } 


slf4j vs log4j

Izvade:



x = 0.7999999999999999  
y = 0.8
false

Šeit atbilde nav tāda, kādu mēs gaidījām, iemesls ir java kompilatora veiktā noapaļošana.

Noapaļošanas kļūdas iemesls

Float un Double datu tipi īsteno IEEE peldošā komata 754 specifikāciju. Tas nozīmē, ka skaitļi tiek attēloti šādā formā:

SIGN FRACTION * 2 ^ EXP 

0,15625 = (0,00101)2kas peldošā komata formātā tiek attēlots kā: 1.01 * 2^-3
Ne visas daļas var precīzi attēlot kā divu pakāpju daļu. Vienkāršs piemērs: 0,1 = (0,000110011001100110011001100110011001100110011001100110011001…)2 un tādējādi to nevar saglabāt peldošā komata mainīgajā.

Vēl viens piemērs:

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);  } } 

Izvade:

x = 0.7999999999999999  
y = 0.8
false

Vēl viens piemērs:

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);  } } 

Izvade:

a = 0.09999999999999998

Kā labot noapaļošanas kļūdas?

  • Noapaļo rezultātu: Funkciju Round() var izmantot, lai samazinātu peldošā komata aritmētiskās uzglabāšanas neprecizitātes ietekmi. Lietotājs var noapaļot skaitļus līdz skaitļiem aiz komata, kas nepieciešams aprēķinam. Piemēram, strādājot ar valūtu, jūs, iespējams, noapaļojat līdz 2 zīmēm aiz komata.
  • Algoritmi un funkcijas: Izmantojiet skaitliski stabilus algoritmus vai izstrādājiet savas funkcijas, lai risinātu šādus gadījumus. Varat saīsināt/apaļot ciparus, par kuriem neesat pārliecināts, ka tie ir pareizi (varat arī aprēķināt operāciju ciparu precizitāti)
  • BigDecimal klase: Jūs varat izmantot java.math.BigDecimal klase, kas ir paredzēta, lai sniegtu mums precizitāti, īpaši lielu daļskaitļu gadījumā. Šī programma parāda, kā kļūdu var novērst:
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);  } } 


Izvade:

0.1

Šeit a = a.setScale(1 RoundingMode.HALF_UP);

Kārtas alīdz 1 zīmei aiz komata, izmantojot noapaļošanas režīmu HALF_UP. Tādējādi BigDecimal izmantošana nodrošina precīzāku aritmētisko un noapaļošanas darbību kontroli, kas var būt īpaši noderīga finanšu aprēķinos vai citos gadījumos, kad precizitātei ir izšķiroša nozīme.

Svarīga piezīme:

Math.round noapaļo vērtību līdz tuvākajam veselam skaitlim. Tā kā 0,10 ir tuvāk 0 nekā 1, tas tiek noapaļots līdz 0. Pēc noapaļošanas un dalīšanas ar 1,0 rezultāts ir 0,0. Tātad jūs varat pamanīt atšķirību starp rezultātiem, izmantojot BigDecimal klasi un funkciju Maths.round.

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);  } } 

Izvade:

0.0