Двойной вопрос вычитания точности - PullRequest
17 голосов
/ 28 марта 2012

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

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 не является типом данных для этой работы, что я должен использовать?

Ответы [ 4 ]

20 голосов
/ 28 марта 2012

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

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

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

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

2 голосов
/ 28 марта 2012

double и float не совсем реальные числа .

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

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

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

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

1 голос
/ 28 марта 2012

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

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

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

0 голосов
/ 15 августа 2014
 //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();  
 }
...