double arithmetic and equality in Java

ⅰ亾dé卋堺 提交于 2019-12-17 16:56:08

问题


Here's an oddity (to me, at least). This routine prints true:

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

But this routine prints false:

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

Anyone care to explain what's going on here? I'm guessing it has something to do with integer arithmetic for ints posing as floats. Also, are there other bases (other than 10) that have this property?


回答1:


1.0 has an exact binary representation. 0.1 does not.

perhaps you are asking why 0.1 is not stored as a mantissa of 1 and an exponent of -10? but that's not how it works. it's not a decimal number plus an exponent, but a binary number. so "times 10" is not a natural thing.

sorry, maybe the last part is unclear. it's better to think of the exponent as a shift of bits. no shift of bits will convert an infinite sequence like 0.1 (decimal) into a finite one.




回答2:


Edit
I stand corrected by Andrew. Thank you!

Java follows IEEE 754 with a Base of 2, so it cannot represent 0.1 correctly (it is aprox. 0.1000000000000000055511151231257827021181583404541015625 or 1.1001100110011001100110011001100110011001100110011010 * 2^-4 in IEEE) which you can find out based on the binary representation of the double like this (bit 63 = sign, bits 62-52 = exponent and bits 51-0 being the mantissa):

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

I just got carried away by the results and I thought for a moment that the floats in Java are working with a Base of 10 in which case it would have been possible to represent 0.1 just fine.

And now to hopefully clear the things once and for all, here's what goes on:

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

Outputs:

0.1
0.10000000000000009
1.1
1.0
0.1000000000000000055511151231257827021181583404541015625
0.100000000000000088817841970012523233890533447265625
1.100000000000000088817841970012523233890533447265625
1

So what happens? 1.1 - 1.0 is equivalent to:
1.100000000000000088817841970012523233890533447265625 - 1 (Java can't represent 1.1 precisely) which is 0.100000000000000088817841970012523233890533447265625 and this is different than the way Java represent 0.1 internally (0.1000000000000000055511151231257827021181583404541015625)

If you're wondering why the result of the subtraction is being displayed as 0.10000000000000009 and the "0.1" is displayed as it is, have a look over here




回答3:


This comes up in currency calculations all the time. Use BigDecimal if you need exact numerical representation at the cost of not having hardware enabled performance, of course.



来源:https://stackoverflow.com/questions/7289361/double-arithmetic-and-equality-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!