Проблема с двойной вычитанием

Мой коллега сделал этот эксперимент:

public class DoubleDemo {

      public static void main(String[] args) {
           double a = 1.435;
           double b = 1.43;
           double c = a - b;
           System.out.println(c);
      }
 }

Для этой операции первого класса я ожидал этот вывод:

0.005

Но неожиданно вывод был:

0.0050000000000001155

Почему двойной провал в такой простой операции? И если double не является типом данных для этой работы, что я должен использовать?

Ответ 1

double внутренне хранится как дробь в двоичном виде 1/4 + 1/8 + 1/16 + ...

Значение 0.005 - или значение 1.435 - не может быть записано как точная дробь в двоичном формате, поэтому double не может сохранить точное значение 0.005, а вычитаемое значение неточно.

Если вам нужна точная десятичная арифметика, используйте BigDecimal.

Вы также можете найти эту статью полезное чтение.

Ответ 2

double и float не точно действительные числа.

Существует бесконечное число действительных чисел в любом диапазоне, но только конечное число бит для их представления!. По этой причине ожидается округление ошибок для double и floats.

Число, которое вы получаете, является самым близким числом, которое может быть представлено двойным в представлении с плавающей запятой.

Для получения дополнительной информации вы можете прочитать в этой статье [предупреждение: может быть высокоуровневым].

Вы можете использовать BigDecimal, чтобы получить ровно десятичное число [но вы снова столкнетесь с ошибками округления, когда вы попытаетесь получить 1/3].

Ответ 3

double и float арифметика никогда не будет точно верна из-за округления, которое происходит "под капотом".

По существу удваивает и плавает может иметь бесконечное количество десятичных знаков, но в памяти они должны быть представлены некоторым количеством реальных бит. Поэтому, когда вы выполняете эту десятичную арифметику, происходит процедура округления и часто отбрасывается очень маленькой суммой, если вы учитываете все десятичные знаки.

Как было предложено ранее, если вам нужны абсолютно точные значения, используйте BigDecimal, который сохраняет свои значения по-разному. Здесь API

Ответ 4

 //just try to make a quick example to make b to have the same precision as a has, by using BigDecimal

 private double getDesiredPrecision(Double a, Double b){
     String[] splitter = a.toString().split("\\.");
     splitter[0].length();   // Before Decimal Count
     int numDecimals = splitter[1].length(); //After Decimal Count

     BigDecimal bBigDecimal = new BigDecimal(b);
     bBigDecimal = bBigDecimal.setScale(numDecimals,BigDecimal.ROUND_HALF_EVEN);

     return bBigDecimal.doubleValue();  
 }