Math.random () and Curiosity with Precise Loss

The following does not compile:

int result = Math.random() + 1; error: possible loss of precision int result = Math.random() + 1; ^ required: int found: double 

but the following does :

 int result = 0; result += Math.random() + 1; 

Why?

Putting the compiled code in a nested loop, we can expect that the result will increase by 1 with each iteration, because Math.random () always returns double, whose value is less than 1, and when added to an integer, the fractional part will be lost due to loss of accuracy. Run the following code and you will see an unexpected result:

 public class MathRandomCuriosity { public static void main(String[] args) { int result = 0; for (int i = 0; i < 10; i++) { // System.out.println(result); for (int j = 0; j < 20; j++) { // System.out.println(result); for (int k = 0; k < 300; k++) { // System.out.println(result); for (int m = 0; m < 7000; m++) { result += Math.random() + 1; } } } } System.out.println(result); } } 

With 10 * 20 * 300 * 7000 = 42,000,000 iterations, the result should be 42,000,000. But this is not so! The result varies, for example, 42 000 007 against 42 000 006 against 42 000 010, etc.

Why?

By the way ... this is not a code that is used anywhere, it is the result of a quiz that I received in the newsletter. The reason for nested loops is that I can view the value of the result at intervals.

+4
source share
3 answers

Assigned operators of type += perform implicit translation.

Note: in this case, Math.random() will be rounded to 0 each time, which is a significant loss of precision .;)

However, Math.random() + 1 has a very low probability of rounding to 2. for example. 1.999999 will be rounded to 1, and 1.9999999999999999 will be rounded to 2 (but the double + operator, not cast to int ).

 long l = Double.doubleToLongBits(1.0); double d0_999etc = Double.longBitsToDouble(l -1); System.out.println("The value before 1 is " +d0_999etc+" cast to (int) is "+ (int) d0_999etc); System.out.println("The value before 1, plus 1 is " +(1+d0_999etc)+" cast to (int) is "+(int)(1 +d0_999etc)); 

prints

 The value before 1 is 0.9999999999999999 cast to (int) is 0 The value before 1, plus 1 is 2.0 cast to (int) is 2 
+12
source

Details of the IEEE mathematical implementation indicate a loss of accuracy and unreliable results from the double / float to integer conversion. For example, I once found code comparing floating point numbers:

 int x = 0; if (a <= b) { x = y; } if (a > b) { x = z; } 

Sometimes the result was x == 0 for example, a number that was not detected by any of the operators, I had to rewrite the code as:

 int x = 0; if (a <= b) { x = y; } else { x = z; } 
+1
source

By definition, Math.random() returns a double result from 0.0 to 1.0. The operation Math.random() + 1 creates a double result, which is then assigned to the int variable, which produces an integer result. At each iteration, the result is 1 if Math.random() does not return exactly 1.0. The likelihood that this will happen is very low, but still exists. It seems like it is 1/6000. For this reason, some iterations of the loop add 2 to your result.

So, accuracy is not lost here. Everything happens according to the specification.

-1
source

Source: https://habr.com/ru/post/1384934/


All Articles