Mur*_*ray 12 java floating-point
我正在摆弄无限循环来测试其他代码/我的理解,并遇到了这种奇怪的行为.在下面的程序中,从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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
res*_*man 16
这是因为floats具有可以表示的最小精度,随着floats值变大而减小.在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;
}
}
Run Code Online (Sandbox Code Playgroud)
你可以看到这种情况发生.似乎它一旦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);
Run Code Online (Sandbox Code Playgroud)
你可以看到2 ^ 24之后的下一个可表示的值是2 ^ 24 + 2.
为了更好地解释为什么会发生这种情况,以及为什么它开始在何处发生,请参阅此答案
| 归档时间: |
|
| 查看次数: |
232 次 |
| 最近记录: |