当我运行下面发布的程序时,我得到了一些奇怪的结果。
该程序假设以i = 5,000,000的倍数打印消息,但是,当i不等于500万的倍数时,它有时会打印消息。
当我将numberOfTests从5000万更改为500万时,程序运行正常。此外,如果我将它从float更改为double,程序也可以正常工作。
我目前的代码出了什么问题?浮动变量不起作用吗?为什么会这样?我怎么能在将来防止这种情况发生?
public static void main(String[] args)
{
final int numberOfTests = 50 * 1000 * 1000;
final float IncrementsPercentageToPrintResults = .10f;
for(int i = 1; i <= numberOfTests; i++)
{
if(i % (IncrementsPercentageToPrintResults * numberOfTests) == 0)
{
System.out.println("Currently at " + (int) (((float) i / numberOfTests) * 100) + "%." );
System.out.println(" i = " + i);
}
}
}
Output:
Currently at 10%.
i = 5000000
Currently at 20%.
i = 10000000
Currently at 30%.
i = 15000000
Currently at 40%.
i = 19999999
Currently at 40%.
i = 20000000
Currently at 40%.
i = 20000001
Currently at 50%.
i = 24999999
Currently at 50%.
i = 25000000
Currently at 50%.
i = 25000001
Currently at 60%.
i = 29999999
Currently at 60%.
i = 30000000
Currently at 60%.
i = 30000001
Currently at 70%.
i = 34999998
Currently at 70%.
i = 34999999
Currently at 70%.
i = 35000000
Currently at 70%.
i = 35000001
Currently at 70%.
i = 35000002
Currently at 80%.
i = 39999998
Currently at 80%.
i = 39999999
Currently at 80%.
i = 40000000
Currently at 80%.
i = 40000001
Currently at 80%.
i = 40000002
Currently at 90%.
i = 44999998
Currently at 90%.
i = 44999999
Currently at 90%.
i = 45000000
Currently at 90%.
i = 45000001
Currently at 90%.
i = 45000002
Currently at 100%.
i = 49999998
Currently at 100%.
i = 49999999
Currently at 100%.
i = 50000000
答案 0 :(得分:6)
这是因为名为numeric promotion的事情。任何时候有一个涉及两种不同类型数字的表达式,“最窄”的数字将被提升为“最宽”的数字。
你有一个这样的表达式:
if(i % (IncrementsPercentageToPrintResults * numberOfTests) == 0)
这里,IncrementPercentageToPrintResults
是一个浮点数,因此表达式中的每个其他数字都会被提升为浮点数。
问题在于,对于大数字,float实际上不如int精确。
实际上,对于浮点数,在±2 ^ 24(±16,777,216)之后,它不能再代表奇数。所以像19,999,999这样的数字会变为20,000,000。数字越大,浮点变得越不精确。
有许多解决方案,这里最好的解决方案是不要将int乘以浮点分数来进行除法。只需将numberOfTests
除以10:
if(i % (IncrementsPercentageToPrintResults / 10) == 0)
另一个OK解决方案是使用double而不是float,因为double可以完全表示int中的所有值。
另一个是,如果你真的想要,将生成的float转换为int:
(int)(IncrementsPercentageToPrintResults * numberOfTests)
但是,乘法表达式仍然四舍五入为浮点数,因此只能在此处起作用,因为float可以精确地表示值5,000,000。有时像这样的演员是必要的,但在这里不是。只需使用除法或加倍。
两个强制性链接是:
答案 1 :(得分:2)
问题出在以下几行:
i % (IncrementsPercentageToPrintResults * numberOfTests)
(IncrementsPercentageToPrintResults * numberOfTests)
会产生浮点值,因为IncrementsPercentageToPrintResults
会将其强制转换为浮点值。浮点值不应与模数运算符一起使用,因为计算机处理浮点值的方式。快速修复如下:
i % (int) (IncrementsPercentageToPrintResults * numberOfTests)
这会将结果转换为修正此问题的整数。