for循环中奇怪的重复次数

strange number of repetitions in for loop

我写了这个小程序来计算圆周率。

在尝试使用代码并尝试找到最准确的结果时,我发现我的计算机无法计算结果。它可以在几秒钟内重复 33554430 次,但是如果我将 for 循环增加到 33554431 它没有输出任何东西。

所以33554430是一个特殊号码吗?

public class CalculatePi{
    public static void main(String[] args){
        float pi=0;
        int sign=1;
        for(float i=1; i <= 33554430; i+=2){
            pi += (sign*(1.0/i));
            sign*= -1;
        }
        pi *= 4;
        System.out.println(pi);
     }

}

此版本适用于两者。这是导致问题的浮动循环变量:

public static void main(String[] args){
        float pi=0;
        int sign=1;
        for(int i=1; i <= 33554430; i+=2){
            pi += (sign*(1.0/(float)i));
            sign*= -1;
        }
        pi *= 4;
        System.out.println(pi);
    }

可能是因为这个 issue:

The finite nonzero values of any floating-point value set can all be expressed in the form s · m · 2(e - N + 1), where s is +1 or -1, m is a positive integer less than 2N, and e is an integer between Emin = -(2K-1-2) and Emax = 2K-1-1, inclusive, and where N and K are parameters that depend on the value set.

你的循环每次递增 2。

2 * 33554430 = 67108860

2^26 = 67108864

也许Java在32位系统中使用26位尾数和6位指数来存储浮点数?

你得到了无限循环,因为在比较 i <= 33554431 期间,int33554431promoted to a float 值,对于浮点数是 "too precise"并且实际上等于 33554432。 然后,当您尝试将值增加 +2 时,float 不够精确,无法从值 33554432 增加。为了说明我的观点:

float f = 33554432;
System.out.println(f); //33554432
f += 2;
System.out.println(f); //33554432

所以值 f 由于其精度限制不会增加。如果你将它增加,比如 11,你会得到 33554444(而不是 33554443),因为这是可以用该精度表达的最接近的数字。

So is 33554430 a special number?

有点像,不是 33554430,而是 33554432。浮点数的第一个 "special number" 是 16777217,这是第一个不能表示为 float 的正整数(等于 16777216 作为浮点数)。因此,如果您将 i 变量递增 1,这就是您遇到的问题。现在,由于您要递增 2,因此卡住的数字是 16777216 * 2 = 33554432

public class CalculatePi{
    public static void main(String[] args){
        float pi=0;
        int sign=1;
        for(float i=1; i <= 33554431; i+=2){
            pi += (sign*(1.0/i));
            sign*= -1;
        if( i > 33554410) System.out.println(i);
        }
        pi *= 4;
        System.out.println(pi);

        System.out.println((float)33554431);
        System.out.println((float)33554432);
        System.out.println((float)33554434);
     }

}

你在for循环中比较float和int。当您将 33554431 (它是 int 值)转换为 float 时,您会得到 3.3554432E7.

这是关于准确度、精确度的。当你 运行:

System.out.println((float)33554431);  // -> 3.3554432E7
System.out.println((float)33554432);  // -> 3.3554432E7
System.out.println((float)33554434);  // -> 3.3554432E7

全部 3 打印 3.3554432E7,这意味着当你将 33554432 的浮点值增加 2 时,你会得到 3.3554432E7,完全相同的值,并且你的循环永远 运行s .