Math.random()和精度损失的好奇心

时间:2011-12-06 15:35:50

标签: java math

以下内容无法编译:

int result = Math.random() + 1;

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

但以下 编译:

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

为什么?

将可编译代码放入嵌套循环中,每次迭代都会期望结果递增1,因为Math.random()总是返回一个小于1的double,当加到整数时,小数部分将是因精确损失而丢失。运行以下代码并查看意外结果:

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);
  }
}

使用10 * 20 * 300 * 7000 = 42,000,000次迭代,结果应为42,000,000。但事实并非如此!结果各不相同,即42,000,007 vs. 42,000,006 vs. 42,000,010等。

为什么?

顺便说一句......这不是在任何地方使用的代码,而是来自我在简报中收到的测验。嵌套循环的原因是我可以间隔查看结果的值。

3 个答案:

答案 0 :(得分:12)

+=等分配的运算符执行隐式转换。

注意:在这种情况下,Math.random()每次都会向下舍入为0,这会严重损失精度。 ;)

然而Math.random() + 1被舍入到2的可能性非常小。 1.999999将四舍五入为1,但1.9999999999999999将四舍五入为2(但double +运算符而不是转换为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));

打印

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

答案 1 :(得分:1)

IEEE数学实现的细节指出了从双/浮点到整数转换的精度损失和不可靠结果。例如,我曾经找到比较浮点数的代码:

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

有时结果是x == 0,例如,if if语句都没有捕获到的数字,我不得不重写代码:

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

答案 2 :(得分:-1)

根据定义,Math.random()会将double结果从0.0返回到1.0。操作Math.random() + 1创建双重结果,然后将其分配给int变量,从而生成整数结果。在每次迭代时,结果为1,除非Math.random()正好返回1.0。它会发生的可能性非常低,但仍然存在。从统计上看,这似乎是1/6000。这就是某些循环迭代为结果添加2的原因。

所以,这里并没有失去精确度。一切都按照规范发生。