简体   繁体   中英

Strange behavior of float point

I understand random.Float() bring back a float number between 0.0 to 0.999...

but for an unknown reason, almost the same code brings back 2 different answers.

I want to understand why t1 could random 1.0 and t2 can not.

I tried to check for a similar question but could not find out anything similar

    Random rand = new Random();
    for (int i = 0; i < 1000000000; i++) {
        float t1 = 0.9f + rand.nextFloat() * 0.1f;
        float t2 = 0.1f + rand.nextFloat() * 0.9f;
        if (t1 == 1.0f) {
            System.out.println("t1 " + t1);
        }
        if (t2 == 1.0f) {
            System.out.println("t2 " + t2);
        }
    }

no error messages I just can not understand why t1 bring back sometimes 1.0 number and t2 does not.

Edit: t1 generate maxmium of 0.999... *0.1 which is 0.0999... + 0.9 = 0.9999... t2 generate maxmimum of 0.999... *0.9 which is 0.8999... + 0.1 = 0.9999... Both should be the same no?

The primary cause of the difference is that .9 + x •.1 is larger than .1 + x • .9 for any x < 1, and Random.nextFloat returns values less than 1. For the largest value it returns, the former expression is so close to 1 that rounding it to float produces 1, but the latter expression is farther from 1, and rounding it to float produces the next float below 1.

The largest value that Random.nextFloat returns is Random.nextfloat 16777215/16777216. Let M be this maximum, and let d = 1 − M = 1/16777216.

If we calculated t1 using real numbers, its maximum value would be .9 + M •.1. = .9 + (1− d )•.1 = 1−.1• d . Similarly, t2 would be .1 + M •.9 = .1 + (1- d )•.9 = 1-.9• d .

Now we can easily see that the maximum of t1 (if calculated with real numbers) is .1• d away from 1, but t2 is .9• d away from 1.

A reason the largest value Random.nextFloat returns is 16777215/16777216 is that it is the largest float less than 1. The precision of the float format is such that the steps between representable values leading up to 1 are 1/16777216. This means that the two representable values in this neighborhood are 16777215/16777216 and 1, and d is the distance between them. Our calculations above show us the maximum value of t1 is just .1• d away from 1. So, when it is rounded to float , 1 is the nearest float .

In contrast, the maximum value of t2 is .9• d away from 1. This means it is just .1• d away from 16777215/16777216. So, when it is rounded to float , 16777215/16777216 is the nearest float .

That is using real-number arithmetic. Below, I will show the floating-point arithmetic. It has rounding errors, but these turn out to be small enough not to change the results.

In Java , the source texts .1f and .9f are converted to float , which yield 0.100000001490116119384765625 and 0.89999997615814208984375. The arithmetic in the expressions is performed using double , and then the result is rounded to float for assignment to t1 or t2 .

The largest that t1 can be may be calculated:

  • Substituting the largest return value of nextFloat yields 0.9f + 16777215./16777216 * 0.1f; .
  • Evaluating in double arithmetic yields 0.999999971687793642871611154987476766109466552734375.
  • Converting to float yields 1, because that double value is between 0.999999940395355224609375 (the next lower float value) and 1 (the next higher), and it is closer to the latter than to the former.

The largest that t2 can be may be calculated:

  • Substituting the largest return value of nextFloat yields 0.1f + 16777215./16777216 * 0.9f; .
  • Evaluating in double arithmetic yields 0.99999992400407933246242464520037174224853515625.
  • Converting to float yields 0.999999940395355224609375.

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