40

I wrote a program to calculate the PI number with Leibniz formula:

[Leibniz formula]

I wrote a for-loop with type of initialization is "int" , the loop works fine but when i changed the initialization type to "long" the result is changed. This only happens when the loop times over a billion. This makes the "int - loop" calculates PI more accurate than "long - loop". I don't know why this happens. Please help me to understand this issue. Thanks! and here is my code.

public static void main(String[] args) {
    double result1 = 0;
    double result2 = 0;
    double sign = 1;

    for (int i = 0; i <= 1607702095; i++) {
        result1 += sign/(2 * i + 1);
        sign *= -1;
    }
    sign = 1;

    for (long j = 0; j <= 1607702095; j++) {
        result2 += sign/(2 * j + 1);
        sign *= -1;
    }

    System.out.println("result1  " + result1 * 4);
    System.out.println("result2  " + result2 * 4);
    System.out.println("pi       " + Math.PI);
}

And the result is:

result1  3.141592653576877
result2  3.1415926529660116
pi       3.141592653589793
Raedwald
  • 46,613
  • 43
  • 151
  • 237
programer310
  • 399
  • 3
  • 8
  • 1
    @programer310 That would be nice :). You now see how a good question looks like and you can see, that other users appreciate it with upvotes. Btw, when you click on [edit], when you can see, how it was formatted (if you're curious). – Tom Jan 05 '16 at 11:35
  • 3
    As you already know, there is an integer overflow. But be careful with initializations too: `sign` must be reset to 1 before the second loop – edc65 Jan 05 '16 at 14:10
  • 1
    Now here is an interesting question for you. Suppose you do the computations **in several different orders**. You are starting with large things and then adding smaller and smaller things to them. Do you get a different result if you start on the small end and gradually add larger and larger things? What if you do all the negatives and then all the positives? In "real" arithmetic the order in which you perform a series of additions does not matter. Does it matter in addition in Java? If there is a difference, can you deduce *why* there is a difference? – Eric Lippert Jan 05 '16 at 16:03
  • @edc65 - Very good point - I was trying to figure out how the `long` loop could possibly be *less* accurate than the `int` one. That's almost certainly it, because the answer is lower, as would be expected if your first (largest value) loop estimated down instead of up. – Darrel Hoffman Jan 05 '16 at 16:44
  • @edc65 - Thanks for your comment, i edited my code as your advise. Actually the result does't change because after the first loop the "sign"' value already equals to 1. – programer310 Jan 06 '16 at 02:02
  • The "correct" answer for $\sum_{n=0}^1607702095 \frac{(-1)^n}{2n+1}$ is $3.141592652967784$ (using 53-bit arithmetic, as you have). The *finite* sum can be expressed as $\frac{\pi}{4}$ plus a term containing relatively exotic [polygamma functions](http://mathworld.wolfram.com/PolygammaFunction.html), for which there are very fast evaluation methods. I suspect you are seeing precision loss due to loss of carries from the right. That is, your large initial summands truncate your late terms. Try summing in reverse order. – Eric Towers Jan 06 '16 at 07:52

5 Answers5

34

Actually, your first loop would have int overflow in the calculation of (2 * i + 1) when i is large enough, so I wouldn't rely on the output of it.

The second loop, on the other hand, produces a more correct output, since (2 * j + 1) doesn't overflow, since it performs long multiplication.

This makes the "int - loop" calculates PI more accurate than "long - loop"

That's probably just a coincidence, since the calculations in the int loop overflow.

Eran
  • 387,369
  • 54
  • 702
  • 768
12

Because you are getting overflow at the line

result1 += sign/(2 * i + 1);

Where the value of 2*i cross the max integer value

int range is -2,147,483,648 to 2,147,483,647 but when you do 2*i for greater value it crosses that range.

Better to be stick with long and that gives you correct output.

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
11

2 * i when i is close to the end of your loop will overflow the max value for an int which is 2147483647.

Using a long that operation doesn't overflow.

The correct procedure is using a long type. Probably because values are added and removed around the correct PI for some strange behavior the overflows momentarily compute to a value closer to the right PI.

I suppose that change the limit of the for loop of few values will change the final result to a value that is more far from the right PI.

Captain Jack Sparrow
  • 971
  • 1
  • 11
  • 28
Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56
10

You have integer overflow.

The max capacity of a signed int is (2^31)-1, or 2,147,483,647.

(1,607,702,095 * 2) is 3215404190, which is bigger than 2,147,483,647.

When you change i to a long you increase the capacity of i to (2^63)-1.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
nicomp
  • 4,344
  • 4
  • 27
  • 60
7

Noticed everyone is pointig the integer overflow, but you might want a solution. (If you already have one, please ignore the following :) )

Having the overflow in the (2 * i + 1) part of the code, you should max the i in the for loop to (Integer.MAX_VALUE / 2 - 1), which results in:

for (int i = 0; i <= (Integer.MAX_VALUE / 2 - 1); i++) {
    result1 += sign/(2 * i + 1);
    sign *= -1;
}

You can also do that to the long part with (Long.MAX_VALUE / 2 - 1) but it will be running for a VERY LONG time.

JordiVilaplana
  • 485
  • 3
  • 9