logo

„Java“ klaidų apvalinimas

Norint sutankinti daugybę begalinių realiųjų skaičių į baigtinį skaičių bitų, reikia apytiksliai pateikti. Dauguma programų išsaugo sveikųjų skaičių skaičiavimo rezultatus ne daugiau kaip 32 arba 64 bitus. Atsižvelgiant į bet kokį fiksuotą bitų skaičių, dauguma skaičiavimų su realiais skaičiais duos kiekius, kurių negalima tiksliai atvaizduoti naudojant tiek bitų. Todėl slankiojo kablelio skaičiavimo rezultatas dažnai turi būti suapvalintas, kad tilptų atgal į baigtinį vaizdą. Ši apvalinimo paklaida yra būdingas slankiojo kablelio skaičiavimo bruožas. Todėl atliekant skaičiavimus slankiojo kablelio skaičiais (ypač jei skaičiavimai atliekami pinigais), turime pasirūpinti programavimo kalbos apvalinimo klaidomis. Pažiūrėkime pavyzdį:

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


sql serverio pivotas

Išvestis:



x = 0.7999999999999999  
y = 0.8
false

Čia atsakymas nėra toks, kokio tikėjomės, o priežastis yra „Java“ kompiliatoriaus atliktas apvalinimas.

Apvalinimo klaidos priežastis

Slankaus ir dvigubo duomenų tipai įgyvendina IEEE slankiojo kablelio 754 specifikaciją. Tai reiškia, kad skaičiai pateikiami tokia forma:

SIGN FRACTION * 2 ^ EXP 

0,15625 = (0,00101)2kuris slankiojo kablelio formatu vaizduojamas kaip: 1.01 * 2^-3
Ne visos trupmenos gali būti tiksliai pavaizduotos kaip dviejų laipsnio trupmena. Kaip paprastas pavyzdys 0.1 = (0.000110011001100110011001100110011001100110011001100110011001…)2 ir todėl negali būti saugomi slankiojo kablelio kintamajame.

Kitas pavyzdys:

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

Išvestis:

x = 0.7999999999999999  
y = 0.8
false

Kitas pavyzdys:

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

Išvestis:

a = 0.09999999999999998

Kaip ištaisyti apvalinimo klaidas?

  • Apvalinti rezultatą: Funkcija Round() gali būti naudojama norint sumažinti bet kokį slankiojo kablelio aritmetinio saugojimo netikslumo poveikį. Vartotojas gali suapvalinti skaičius iki skaičių po kablelio, kurio reikia skaičiavimui. Pavyzdžiui, dirbdami su valiuta greičiausiai suapvalinsite iki 2 skaitmenų po kablelio.
  • Algoritmai ir funkcijos: Naudokite skaitmeniniu požiūriu stabilius algoritmus arba kurkite savo funkcijas, kad galėtumėte tvarkyti tokius atvejus. Galite sutrumpinti/apvalinti skaitmenis, kurių teisingumu nesate tikri (taip pat galite apskaičiuoti operacijų skaitinį tikslumą)
  • BigDecimal klasė: Galite naudoti java.math.BigDecimal klasė, kuri skirta suteikti mums tikslumą, ypač esant dideliems trupmeniniams skaičiams. Ši programa parodo, kaip galima pašalinti klaidą:
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);  } } 


Išvestis:

0.1

Čia a = a.setScale(1 RoundingMode.HALF_UP);

Raundai aiki 1 dešimtosios dalies naudojant HALF_UP apvalinimo režimą. Taigi naudojant BigDecimal galima tiksliau valdyti aritmetines ir apvalinimo operacijas, kurios gali būti ypač naudingos atliekant finansinius skaičiavimus ar kitais atvejais, kai tikslumas yra labai svarbus.

Svarbi pastaba:

Math.round suapvalina reikšmę iki artimiausio sveikojo skaičiaus. Kadangi 0,10 yra arčiau 0 nei 1, jis suapvalinamas iki 0. Suapvalinus ir padalijus iš 1, rezultatas yra 0,0. Taigi galite pastebėti skirtumą tarp išėjimų naudodami BigDecimal klasę ir Maths.round funkciją.

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

Išvestis:

0.0