以下内容无法编译:
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等。
为什么?
顺便说一句......这不是在任何地方使用的代码,而是来自我在简报中收到的测验。嵌套循环的原因是我可以间隔查看结果的值。
答案 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的原因。
所以,这里并没有失去精确度。一切都按照规范发生。