Whats wrong with this simple 'double' calculation? [duplicate]

♀尐吖头ヾ 提交于 2019-11-27 04:40:18

The problem

In binary 2.64 is 10.10100011110101110000101000111101 recurring, in other words not exactly representable in binary, hence the small error. Java is being kind to you with d3 but as soon as actual calculations are involved it has to fall back on the real representation.

Binary Calculator

Further more:

2.64= 10.10100011110101110000101000111101
4.64=100.1010001111010111000010100011110 

Now, even though the .64 is the same in both cases, it is held to different precisions because the 4=100 uses up more of the double significant figures than 2=10, so when you say 4.64-2.0 and 2.64 the .64 is represented with a different rounding error in both cases, this lost information cannot be recovered for the final answer.

N.B. I'm not using the double number of significant figures here, just whatever the binary calculator would produce, however the effect is the same whatever the number of significant figures

Never assume double values are exact (although their inaccuracies are microscopic and caused only because certain numbers can't be exactly expressed in binary).


Floating point numbers aren't exact, but only from a decimal point of view

While you should always expect that doubles will have small errors for the last few decimal places it would be wrong to think of binary representations as "bad" or worse that decimal.

We are all used to certain numbers (like 1/3 for example) not being exactly representable in decimal and we accept that such a number will end up as 0.333333333333 rather than the true value (which I cannot write down without infinite space); it is within that context that binary numbers cannot be exactly expressed. 1/10 is such a number that cannot be exactly expressed in binary; this suprises us only because we are used to decimal

d1 - d2 returns the exact result of binary float arithmetic and it is 2.6399999999999997 and so it is printed. If you want to round it, you can do it during printing

System.out.printf("d1 - d2 : %.2f",  d4);

or with Commons-Math

d4 = Precision.round(d4, 2);

It's because the errors in the internal representations of 4.64 and 2.0 combine constructively (meaning they make a larger error).

Technically speaking, 2.64 isn't stored exactly, either. However, there is a particular representation that corresponds to 2.64. Think about the fact that 4.64 and 2.0 aren't stored exactly, either, though. The errors in 4.64 and 2.0 are combining to produce an even larger error, one large enough that their subtraction does not give the representation of 2.64.

The answer is off by 3*10^-16. To give something of an example of how that can happen, let's pretend the representation for 4.64 is 2*10^-16 too small and the representation for 2.0 is 1*10^-16 too large. Then you would get

(4.64 - 2*10^-16) - (2.0 + 1*10^-16) = 2.64 - 3*10^-16

So when the calculation is done, the two errors have combined to create an even bigger error. But if the representation for 2.64 is only off by 1*10^-16, then this would not be considered equal to 2.64 by the computer.

It's also possible that 4.64 just has a larger error than 2.64 even if 2.0 has no error. If 4.64's representation is 3*10^-16 too small, you get the same thing:

(4.64 - 3*10^-16) - 2.0 = 2.64 - 3*10^-16

Again, if the representation of 2.64 is only off by 1*10^-16, then this result would not be considered equal to 2.64.

I don't know the exact errors in the real representations, but something similar to that is happening, just with different values. Hope that makes sense. Feel free to ask for clarification.

Mainly because of the fact that double is a double-precision 64-bit IEEE 754 floating point. It's not meant for keeping exact decimal values. That's why doubles are not recommended for exact calculations. Use the String constructor of BigDecimal instead, like:

new BigDecimal("2.64")

Nothing wrong with it. But try using BigDecimal

http://docs.oracle.com/javase/6/docs/api/java/math/BigDecimal.html

Note: double and float are internally represented as binary fractions according to the IEEE standard 754 and can therefore not represent decimal fractions exactly

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