3

When I run the program posted below, I get some weird results.

The program is suppose to print a message at multiples of when i = 5,000,000, however, it sometimes prints the message when i is not equal to a multiple of 5 million.

When I change the numberOfTests from 50 million to 5 million, the program runs fine. Also, if I change it from a float to a double, the program works fine as well.

What's wrong with my current code? Is the floating variable not working? Why is this happening? How can I prevent this in the future?

public static void main(String[] args)
{
    final int numberOfTests = 50 * 1000 * 1000;
    final float IncrementsPercentageToPrintResults = .10f;

    for(int i = 1; i <= numberOfTests; i++)
    {
        if(i % (IncrementsPercentageToPrintResults * numberOfTests) == 0)
        {
            System.out.println("Currently at " + (int) (((float) i / numberOfTests) * 100) + "%." );
            System.out.println(" i = " + i);                
        }   
    }   

}

Output:
Currently at 10%.
 i = 5000000
Currently at 20%.
 i = 10000000
Currently at 30%.
 i = 15000000
Currently at 40%.
 i = 19999999
Currently at 40%.
 i = 20000000
Currently at 40%.
 i = 20000001
Currently at 50%.
 i = 24999999
Currently at 50%.
 i = 25000000
Currently at 50%.
 i = 25000001
Currently at 60%.
 i = 29999999
Currently at 60%.
 i = 30000000
Currently at 60%.
 i = 30000001
Currently at 70%.
 i = 34999998
Currently at 70%.
 i = 34999999
Currently at 70%.
 i = 35000000
Currently at 70%.
 i = 35000001
Currently at 70%.
 i = 35000002
Currently at 80%.
 i = 39999998
Currently at 80%.
 i = 39999999
Currently at 80%.
 i = 40000000
Currently at 80%.
 i = 40000001
Currently at 80%.
 i = 40000002
Currently at 90%.
 i = 44999998
Currently at 90%.
 i = 44999999
Currently at 90%.
 i = 45000000
Currently at 90%.
 i = 45000001
Currently at 90%.
 i = 45000002
Currently at 100%.
 i = 49999998
Currently at 100%.
 i = 49999999
Currently at 100%.
 i = 50000000
user207421
  • 305,947
  • 44
  • 307
  • 483
Programmer
  • 112
  • 2
  • 2
  • 7
  • 1
    I believe that [this link](http://www.youtube.com/watch?v=PZRI1IfStY0&list=UU9-y-6csu5WGm29I7JiwpnA&feature=share&index=7) will shed some light on the matter. – Makoto Feb 23 '14 at 00:59
  • possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Stephen C Feb 23 '14 at 01:17
  • 1
    And the answers are 1) it does incorrect comparison of integer values using fp arithmetic 2) no 3) caused by inherent imprecision of fp types 4) by reading and understanding the various linked resources on fp arithmetic. – Stephen C Feb 23 '14 at 01:27
  • @StephenC I don't think this is a duplicate of that question because there is not an obvious floating point math error here. The problem is that `i` is being promoted and rounded. Maybe this has been answered before but not really in that question which mainly focuses on arithmetic with fractional values. – Radiodef Feb 23 '14 at 01:34
  • Also a duplicate of this: http://stackoverflow.com/questions/10682382/getting-numbers-after-decimal-java – Stephen C Feb 23 '14 at 02:39

2 Answers2

6

This happens because of something called numeric promotion. Any time there is an expression involving two different types of numbers, the "narrowest" numbers are promoted to the "widest" numbers.

You have an expression like this here:

if(i % (IncrementsPercentageToPrintResults * numberOfTests) == 0)

Here, IncrementPercentageToPrintResults is a float so it happens that every other number in the expression gets promoted to float.

The problem is that for large numbers, float is actually less precise than an int.

So actually, for a float, after ±2^24 (±16,777,216), it cannot represent odd numbers anymore. So a number like 19,999,999 gets rounded to 20,000,000. The larger the number is, the more imprecise floating point becomes.

There are a number of solutions and the best one here is to simply not multiply an int by a floating point fraction to do division. Just divide numberOfTests by 10:

if(i % (IncrementsPercentageToPrintResults / 10) == 0)

The other OK solution is to use double instead of float because double can represent all values in an int exactly.

Another is, if you really want, to cast the resulting float to an int:

(int)(IncrementsPercentageToPrintResults * numberOfTests)

However, the multiplication expression is still rounded to float so that only works here because float can represent the value 5,000,000 exactly. Sometimes a cast like that is necessary but here it's not. Just use division or double.

The two obligatory links are:

Radiodef
  • 37,180
  • 14
  • 90
  • 125
2

The problem is with the following line:

i % (IncrementsPercentageToPrintResults * numberOfTests)

(IncrementsPercentageToPrintResults * numberOfTests) results in a float value because IncrementsPercentageToPrintResults will typecast it to a float. Float values shouldn't be used with the modulus operator because of how computers handle float values. A quick fix would be the following:

i % (int) (IncrementsPercentageToPrintResults * numberOfTests)

That'll cast the result to be an integer which fixes this issue.

Programmer
  • 112
  • 2
  • 2
  • 7
GEMISIS
  • 434
  • 3
  • 11