logo

Помилки округлення в Java

Ущільнення багатьох нескінченних дійсних чисел у кінцеву кількість бітів вимагає наближеного представлення. Більшість програм зберігають результат цілочисельних обчислень у 32 або 64 біта максимум. За будь-якої фіксованої кількості бітів більшість обчислень із дійсними числами дадуть величини, які неможливо точно представити за допомогою такої кількості бітів. Тому результат обчислення з плаваючою комою часто потрібно округляти, щоб знову вмістити його в кінцеве представлення. Ця помилка округлення є характерною особливістю обчислень із плаваючою комою. Тому, обробляючи обчислення в числах з плаваючою комою (особливо, якщо обчислення здійснюються в термінах грошей), нам потрібно подбати про помилки округлення в мові програмування. Давайте подивимося на приклад:

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


зробити сценарій оболонки виконуваним

Вихід:



x = 0.7999999999999999  
y = 0.8
false

Тут відповідь не така, як ми очікували, тому що округлення виконано компілятором Java.

Причина помилки округлення

Типи даних Float і Double реалізують специфікацію IEEE 754 з плаваючою комою. Це означає, що числа представлені у такій формі:

SIGN FRACTION * 2 ^ EXP 

0,15625 = (0,00101)2який у форматі з плаваючою комою представляється як: 1,01 * 2^-3
Не всі дроби можна точно представити у вигляді частки степеня двійки. Як простий приклад 0,1 = (0,000110011001100110011001100110011001100110011001100110011001… )2 і тому не може зберігатися всередині змінної з плаваючою комою.

Інший приклад:

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

Вихід:

x = 0.7999999999999999  
y = 0.8
false

Інший приклад:

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

Вихід:

a = 0.09999999999999998

Як виправити помилки округлення?

  • Округліть результат: Функцію Round() можна використовувати для мінімізації будь-яких наслідків неточності зберігання арифметичних чисел з плаваючою комою. Користувач може округлити числа до кількості десяткових знаків, яка потрібна для обчислення. Наприклад, під час роботи з валютою ви, ймовірно, округлили б до 2 знаків після коми.
  • Алгоритми та функції: Використовуйте чисельно стабільні алгоритми або створюйте власні функції для обробки таких випадків. Ви можете скоротити/округлити цифри, у правильності яких ви не впевнені (ви також можете розрахувати числову точність операцій)
  • Клас BigDecimal: Ви можете використовувати java.math.BigDecimal клас, який призначений для надання нам точності, особливо у випадку великих дробових чисел. Наступна програма показує, як можна усунути помилку:
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);  } } 


Вихід:

0.1

тут a = a.setScale(1 RoundingMode.HALF_UP);

раунди aдо 1 десяткового знака з використанням режиму округлення HALF_UP. Таким чином, використання BigDecimal забезпечує більш точний контроль над арифметичними операціями та операціями округлення, що може бути особливо корисним для фінансових розрахунків або інших випадків, коли точність має вирішальне значення.

Важлива примітка:

Math.round округлює значення до найближчого цілого числа. Оскільки 0,10 ближче до 0, ніж до 1, воно округлюється до 0. Після округлення та ділення на 1,0 результат дорівнює 0,0. Таким чином, ви можете помітити різницю між виходами за допомогою класу BigDecimal і функції 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);  } } 

Вихід:

0.0