Проблема с двойной вычитанием
Мой коллега сделал этот эксперимент:
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();
}