我正在摆弄无限循环来测试其他代码/我的理解,并遇到了这种奇怪的行为。在下面的程序中,从0到2 ^ 24的计数在我的机器上需要<100ms,但是计数到2 ^ 25需要更多时间(在写入时,它仍在执行)。
为什么会这样?
这是在Java 1.8.0_101下,在Windows 10的64位副本上。
TestClass.java
public class TestClass {
public static void main(String[] args) {
addFloats((float) Math.pow(2.0, 24.0));
addFloats((float) Math.pow(2.0, 25.0));
}
private static void addFloats(float number) {
float f = 0.0f;
long startTime = System.currentTimeMillis();
while(true) {
f += 1.0f;
if (f >= number) {
System.out.println(f);
System.out.println(number + " took " + (System.currentTimeMillis() - startTime) + " msecs");
break;
}
}
}
}
答案 0 :(得分:16)
这是因为float
具有可以表示的最小精度,随着float
的值变大而减小。在2 ^ 24和2 ^ 25之间的某处,添加一个不再足以将值更改为下一个可表示的最大数字。此时,每次循环时,f
只保留相同的值,因为f += 1.0f
不再更改它。
如果您将循环更改为:
while(true) {
float newF = f + 1.0f;
if(newF == f) System.out.println(newF);
f += 1.0f;
if (f >= number) {
System.out.println(f);
System.out.println(number + " took " + (System.currentTimeMillis() - startTime) + " msecs");
break;
}
}
你可以看到这种情况发生。一旦f
达到2 ^ 24,它似乎就会停止增加。
如果你用2 ^ 25运行它,上面代码的输出将是无穷无尽的“1.6777216E7”。
您可以使用Math.nextAfter
function来测试此值,它会告诉您下一个可表示的值。如果您尝试运行此代码:
float value = (float)Math.pow(2.0, 24.0);
System.out.println(Math.nextAfter(value, Float.MAX_VALUE) - value);
你可以看到2 ^ 24之后的下一个可表示的值是2 ^ 24 + 2。
为了更好地说明为什么会发生这种情况,以及它为什么开始重要,请参阅this answer