двойная арифметика и равенство в Java - PullRequest
1 голос
/ 03 сентября 2011

Вот странность (для меня, по крайней мере). Эта процедура печатает true:

double x = 11.0;
double y = 10.0;
if (x-y == 1.0) {
    // print true
} else {
    // print false
}

Но эта процедура выводит ложь:

double x = 1.1;
double y = 1.0;
if (x-y == 0.1) {
    // print true
} else {
    // print false
}

Кто-нибудь хочет объяснить, что здесь происходит? Я предполагаю, что это как-то связано с целочисленной арифметикой для int s, изображающей из себя float s. Кроме того, есть ли другие базы (кроме 10), которые имеют это свойство?

Ответы [ 3 ]

9 голосов
/ 03 сентября 2011

1.0 имеет точное двоичное представление. 0.1 нет.

возможно, вы спрашиваете, почему 0,1 не хранится как мантисса 1 и показатель степени -10? но это не так. это не десятичное число плюс показатель степени, а двоичное число. так что «раз 10» не является естественной вещью.

извините, возможно, последняя часть неясна. лучше думать об экспоненте как о сдвиге битов. никакое смещение битов не преобразует бесконечную последовательность, такую ​​как 0,1 (десятичная), в конечную.

4 голосов
/ 03 сентября 2011

Редактировать
Я стою исправлено Эндрю.Спасибо!

Java следует IEEE 754 с основанием 2, поэтому он не может правильно представить 0,1 (это приблизительно. 0.1000000000000000055511151231257827021181583404541015625 или 1.1001100110011001100110011001100110011001100110011010 * 2^-4 в IEEE), которое вы можете узнать на основе двоичного представлениядвойного типа (бит 63 = знак, биты 62-52 = экспонента и биты 51-0 - мантисса):

long l = Double.doubleToLongBits(0.1);
System.out.println(Long.toBinaryString(l));

Я просто увлекся результатами и подумал омомент, когда поплавки в Java работают с основанием 10, и в этом случае можно было бы просто представить 0.1 просто отлично.

А теперь, надеюсь, раз и навсегда прояснить ситуацию, вот что происходит:

BigDecimal bigDecimal1 = new BigDecimal(0.1d);
BigDecimal bigDecimal2 = new BigDecimal(1.1d - 1.0);
BigDecimal bigDecimal3 = new BigDecimal(1.1d);
BigDecimal bigDecimal4 = new BigDecimal(1.0d);
System.out.println(bigDecimal1.doubleValue());
System.out.println(bigDecimal2.doubleValue());
System.out.println(bigDecimal3.doubleValue());
System.out.println(bigDecimal4.doubleValue());
System.out.println(bigDecimal1);
System.out.println(bigDecimal2);
System.out.println(bigDecimal3);
System.out.println(bigDecimal4);

Выходы:

0.1
0.10000000000000009
1.1
1.0
0.1000000000000000055511151231257827021181583404541015625
0.100000000000000088817841970012523233890533447265625
1.100000000000000088817841970012523233890533447265625
1

Так что же происходит?1.1 - 1.0 эквивалентно:
1.100000000000000088817841970012523233890533447265625 - 1 (Java не может точно представить 1.1), что равно 0.100000000000000088817841970012523233890533447265625, и это отличается от способа, которым Java представляет 0.1 внутри (0.1000000000000000055511151231257827021181583404541015625)

ЕслиВы задаетесь вопросом, почему результат вычитания отображается как 0.10000000000000009, а "0.1" отображается как есть, посмотрите здесь

0 голосов
/ 03 сентября 2011

Это постоянно встречается в расчетах валют. Используйте BigDecimal , если вам необходимо точное числовое представление за счет отсутствия аппаратной производительности, конечно.

...