1

Code

public class test{
    public static void main(String[] args){
        double first = 3.14 ;
        first++;
        System.out.println(first);
    }
}

Result

ubuntu@john:~/Desktop$ javac test.java 
ubuntu@john:~/Desktop$ java test

Output : 4.140000000000001

I am getting expected answer for almost every other case... Eg : For 4.14 ,result is 5.14...

Why this case is special ?

codebot
  • 2,540
  • 3
  • 38
  • 89
CamundaGuy
  • 83
  • 3
  • 1
    @Kayaman I don't feel this is a duplicate. OP wants to know why 3.14 is special. And it's because the precision of a floating point value changes as you pass a power of two. So this is a far more specific question than the general broken-ness of floating point arithmetic. – Dawood ibn Kareem May 18 '18 at 09:53
  • @DawoodibnKareem fair enough. – Kayaman May 18 '18 at 09:55
  • 3
    I feel OP is expecting some precision in this floating-point operations @DawoodibnKareem . So the previous duplicate covered the problem. I doubt the mathematical reason is asked (and if it is, it will be regretted ;) ). And there is quite enough answers that cover the specific problem but also the general problem of floating point value. So ... – AxelH May 18 '18 at 09:58
  • 1
    For the ones downvoting. Note that the research is not always simple because this usually required specific terms (floating-point, float precision, ...) and the question is nicely written with an example of the problem. That's probably a better question than most I see from rookies. – AxelH May 18 '18 at 10:05
  • @MarkRotteveel Kayaman dupehammered this earlier. But he and I now agree that it's not a dupe, and he reopened it. Would you reconsider your decision to dupehammer this, in light of my first comment above? This question requires a far more detailed answer than the fact that floating point arithmetic sometimes does unexpected things. – Dawood ibn Kareem May 18 '18 at 17:40

2 Answers2

2

There are lots of numbers that can be expressed exactly in decimal, but not exactly in binary. That is, they have a terminating decimal representation, but no terminating binary representation.

To understand this, consider the number 1/3. It doesn't have a terminating decimal representation - we can keep writing 0.3333333333333 for a while, but sooner or later, we have to stop, and we still haven't quite written 1/3.

The same thing happens when we try to write 2.14 in binary. It's 10.001000111... and a bunch more 0s and 1s that eventually start repeating, in the same way as 0.333333 repeats in decimal.

Now a double is just a binary number with 53 significant figures. So it can't store exactly 2.14, but it can get very close. Now see what happens when we start incrementing it.

2.14 = 10.001000111 .... (53 significant figures, 51 of them after the dot)
3.14 = 11.001000111 .... (53 significant figures, 51 of them after the dot)
4.14 = 100.001000111 ... (53 significant figures, 50 of them after the dot)
5.14 = 101.001000111 ... (53 significant figures, 50 of them after the dot)

So we didn't lose any accuracy when we went from 2.14 to 3.14, because the part after the dot didn't change. Likewise when we went from 4.14 to 5.14.

But when we went from 3.14 to 4.14, we lost accuracy, because we needed one extra digit before the dot, so we lost a digit after the dot.

Now Java has a complicated algorithm for figuring out how to display a floating point number. Basically, it picks the shortest decimal representation that's closer to the floating point number you're trying to represent, than to any other floating point number. That way, if you write double d = 2.14;, then you'll get a floating point number that's SO CLOSE to 2.14, that it will always show up as 2.14 when you print it out.

But as soon as you start messing with the digits after the dot, the complexity of Java's printing algorithm kicks in - and the number can end up printed differently from how you expect.

So this won't happen when you increment a double, but don't change the number of digits before the dot. It can only happen when you increment a double past a power of 2; because this changes the number of digits before the dot.

To illustrate this, I ran this code.

    for(int i = 0; i < 1000000000; i++) {
        if ( i + 1 + 0.14 != i + 0.14 + 1 ) {
            System.out.println(i + 0.14 + 1);
        }
    }

and got this output.

4.140000000000001
1024.1399999999999
2048.1400000000003
4096.139999999999
1048576.1400000001
2097152.1399999997
4194304.140000001

Observe that all these discrepant values are just past a power of two.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
1

A float increment does not increment with 1, but with something that is a tiny bit off. So it will go fine for a while, but after some time the answer will not be correct.

This is because of round-off 'problems' with float increments/decrements. It's bad style. ++ and -- are intended to set a value to its next or previous value, like the next or previous integer, the next or previous element in an array (for pointers) for example

'Next' and 'previous' values are not well-defined for floats.

see: Is floating point math broken?

Kevin
  • 11
  • 4
  • That's not totally true, the precision for `double d = 1.0` will be correct, and if you do `double d = 3.14; double inc = 1; d += inc;` this will still not be correct but can see that the original values (3.14 and 1) are correctly represented. That's not the problem with the increment by 1 but how the result is stored. – AxelH May 18 '18 at 10:10
  • 2
    Usually, a float DOES increment with exactly 1. The only case when it won't is if the increment crosses a power of 2 (for example, from 3.14 to 4.14). I might write my own answer later explaining this. But I feel that THIS answer misses the point. – Dawood ibn Kareem May 18 '18 at 10:18
  • @DawoodibnKareem I'd be happy if you wrote a proper response and this could be another go-to duplicate for floating point related questions. Although if the question gets downvoted too much it won't work very well. Maybe we can improve the question a bit too. – Kayaman May 18 '18 at 10:22
  • 1
    @DawoodibnKareem from 3.5 to 4.5 you wouldn't loose any precision. – matt May 18 '18 at 10:26
  • @matt - that's true. I didn't mean to imply that you'd lose precision EVERY time you increment past a power of 2. I'm only saying that you DON'T lose precision if you DON'T pass a power of 2. – Dawood ibn Kareem May 18 '18 at 10:27
  • https://stackoverflow.com/questions/588004/is-floating-point-math-broken?noredirect=1&lq=1 – Kevin May 18 '18 at 10:28
  • 1
    This is easier to understand in decimal. Imagine you start with 1.888, and keep on adding 1, then rounding to 4 significant figures. After 9.888, you get 10.89. After 99.89, you get 100.9. After 999.9, you get 1001. A `float` or a `double` is just a number with a fixed number of significant figures when expressed in binary. – Dawood ibn Kareem May 18 '18 at 10:32