我正在摆弄无限循环来测试其他代码/我的理解,并遇到了这种奇怪的行为.在下面的程序中,从0到2 ^ 24的计数在我的机器上花费<100ms,但是计数到2 ^ 25需要更多的时间(在写入时,它仍在执行).
为什么会这样?
这是在Windows 1.8的64位副本上的Java 1.8.0_101下.
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; } } } }
resueman.. 16
这是因为float
s具有可以表示的最小精度,随着float
s值变大而减小.在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
函数测试此值,该函数告诉您下一个可表示的值.如果您尝试运行此代码:
float value = (float)Math.pow(2.0, 24.0); System.out.println(Math.nextAfter(value, Float.MAX_VALUE) - value);
你可以看到2 ^ 24之后的下一个可表示的值是2 ^ 24 + 2.
为了更好地解释为什么会发生这种情况,以及为什么它开始在何处发生,请参阅此答案
这是因为float
s具有可以表示的最小精度,随着float
s值变大而减小.在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
函数测试此值,该函数告诉您下一个可表示的值.如果您尝试运行此代码:
float value = (float)Math.pow(2.0, 24.0); System.out.println(Math.nextAfter(value, Float.MAX_VALUE) - value);
你可以看到2 ^ 24之后的下一个可表示的值是2 ^ 24 + 2.
为了更好地解释为什么会发生这种情况,以及为什么它开始在何处发生,请参阅此答案