Java Math.random()和精度损失
以下内容未编译:Java Math.random()和精度损失,java,math,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; 为什么? 将可编译代
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);
}
}
公共类数学模型
{
公共静态void main(字符串[]args)
{
int结果=0;
对于(int i=0;i<10;i++)
{
//系统输出打印项次(结果);
对于(int j=0;j<20;j++)
{
//系统输出打印项次(结果);
对于(int k=0;k<300;k++)
{
//系统输出打印项次(结果);
对于(int m=0;m<7000;m++)
{
结果+=Math.random()+1;
}
}
}
}
系统输出打印项次(结果);
}
}
当10*20*300*7000=42000000次迭代时,结果应为42000000次。但事实并非如此!结果各不相同,即42000007对42000006对42000010等
为什么?
顺便说一句……这不是任何地方都在使用的代码,它来自我在时事通讯中收到的一个测验。嵌套循环的原因是,我可以每隔一段时间查看结果的值。像
+=
这样的赋值运算符执行隐式转换
注意:在这种情况下,Math.random()
每次都将四舍五入到0,这是一种严重的精度损失。;)
但是Math.random()+1
被四舍五入为2的可能性很小。e、 g.1.9999999将四舍五入为1,但1.9999999999999将四舍五入为2(但double+
运算符而不是转换为int
)
印刷品
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
IEEE数学实现的细节指出了双精度/浮点到整数转换的精度损失和不可靠结果。例如,我曾经发现比较浮点数的代码:
int x = 0;
if (a <= b)
{
x = y;
}
if (a > b)
{
x = z;
}
根据定义
Math.random()
返回从0.0到1.0的double
结果。操作Math.random()+1
创建双精度结果,然后将其分配给int变量,从而生成整数结果。每次迭代的结果都是1,除非Math.random()
精确返回1.0。这种情况发生的可能性很低,但仍然存在。从统计上看,这大约是6000分之一。这就是某些循环迭代向结果中添加2的原因
所以,这里没有失去精确性。一切都是按照规范进行的。在一般情况下,这个问题已经被反复回答过。为(int i=0;i<42000000;i++)编写
的方法真奇怪。
首先尝试将Math.random()调用强制转换为整数。目前,代码中的“1”字面值被隐式转换为双精度,这意味着精度在某处丢失。是否可能重复@Woot4Moo no?整数的最大值(java)为2147483647。这是4200万的50倍多。非常小的补充:不是Math.random()被截断为0,而是“Math.random()+1”被截断为1。1被提升为double,然后被添加,然后被截断。这并不是说随机数被提前截断了。当我找到System.out.println(0.9999999999999)时,你回答了这个问题代码>是1.0
+1表示速度快。@SeanOwen,我的观点是有一个错误的假设,因为Math.random()
总是四舍五入为0,Math.random()+1
总是四舍五入为1。被四舍五入的不是1.99999999999999。而是1.0D+0.99999999999d=2.0D。事实上,它与+=运算符无关。@jsravn,+=
正在转换为int
,如果没有它,程序将无法编译。我已经澄清了进行舍入的操作。铸造到(int)
总是向下舍入(如数学地板(x))。但是,double
操作取整到最接近的表示。这个答案是错误的。根据规范,Math.random返回一个双精度值,因此我猜这是因为0是有符号的。(也就是说,浮点系统中同时存在一个“+0”和一个“-0”,这两个数字有不同的表示形式)看看这些数字如何与比较运算符交互是一件很有趣的事情。是的,甚至还有一些方法可以将接近零的轮数调整为零。最初的代码是由一位科学家编写的,所以我必须向他解释IEEE数学是如何违反代数表达式的一些基本租户的。虽然这并不能回答我的两个问题,但它非常有趣和有用!非常感谢。我想让大家了解浮点数学的一般问题。Java隐藏了IEEE数学的许多细节,但没有隐藏结果。
int x = 0;
if (a <= b)
{
x = y;
}
if (a > b)
{
x = z;
}
int x = 0;
if (a <= b)
{
x = y;
}
else
{
x = z;
}