Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why wont my for loop add fractions past 1.0/10,000,000.0?

I need to add a series of fractions. I looked through other for loops on adding fractions, but did not come across the problem I am having. The fractions denominator increases by 1.0 each time until it adds up 1.0/1.0 + 1.0/2.0 + .... + 1.0/ 150,000,000.0. My code has worked for me by adding the fractions until I go a little over 10,000,000 in my for loop. Once I go over say 15,000,000 it just doesn't print anything and the program continues to run. What error did I make? Why won't it print the answer when it's above a certain number?

Here is the code:

float sum = 0;
float numerator = 1;
float denominator = 1;

for(float i = 1; i <= 10000000; i++)
{
    sum = ((float)numerator/denominator) + sum;
    denominator++;
}
System.out.println("The sum is " + sum);
like image 723
Drizzy Avatar asked Feb 07 '23 22:02

Drizzy


2 Answers

Adding 1 to a sufficiently large float won't have any effect any more. The problem is the limited precision of a float -- 23 bits. Once you get to about 16 million (about 224), the difference between consecutive float values is greater than 1. You can see this by printing out the result of Math.ulp (unit in last place) value.

if (denominator % 1000 == 0)
    System.out.println("Denominator is " + denominator + ", ulp is " + Math.ulp(denominator));

At the point where the output stops, the ulp is 1.0.

...
Denominator is 1.6775E7, ulp is 1.0
Denominator is 1.6776E7, ulp is 1.0
Denominator is 1.6777E7, ulp is 1.0

At 224, the ulp jumps to 2.0, and adding 1.0 follows IEEE's round towards even rule, and the sum is unchanged.

Change the datatypes to double for a much larger precision (53 bits). Just beware that this change would only prolong the problem until you reach 253. That would also make your program run much longer. And if you really want to go beyond that amount, use BigDecimals.

like image 50
rgettman Avatar answered Feb 10 '23 12:02

rgettman


Instead of writing more comments here are my thoughts:

Floats have limited precision. It means that if you add very, very small numbers, the outcome might not be what you'd expect. It also works the other way - adding 1 to a very large number won't change it's value.

In your program to avoid this problem you should increment integer values and cast them to float before summation. Like this:

public class Sum {

public static void main(String[] args) {
    // TODO Auto-generated method stub


    float sum = 0;
    float numerator = 1;
    int denominator = 1; //changed to int

    for(int i = 1; i <= 10000000; i++)
    {
        sum = (numerator/(float)denominator) + sum; //this casts denominator to float
        denominator++; //this will now increment integer

    }
    System.out.println("The sum is " + sum);




} 
}

Further reading: Floating point on wikipedia

like image 27
Dunno Avatar answered Feb 10 '23 12:02

Dunno