简体   繁体   中英

Floating Point Variable Not Working

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

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:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM