Java For-loop 在改变循环变量类型时改变数值结果
Java For-loop changes numeric result when changing type of loop variable
我写了一个用莱布尼茨公式计算圆周率的程序:
[
我写了一个初始化类型为 "int" 的 for 循环,循环工作正常,但是当我将初始化类型更改为 "long" 时,结果发生了变化。只有当循环次数超过 10 亿次时才会发生这种情况。这使得 "int - loop" 计算 PI 比 "long - loop" 更准确。我不知道为什么会这样。请帮助我理解这个问题。谢谢!这是我的代码。
public static void main(String[] args) {
double result1 = 0;
double result2 = 0;
double sign = 1;
for (int i = 0; i <= 1607702095; i++) {
result1 += sign/(2 * i + 1);
sign *= -1;
}
sign = 1;
for (long j = 0; j <= 1607702095; j++) {
result2 += sign/(2 * j + 1);
sign *= -1;
}
System.out.println("result1 " + result1 * 4);
System.out.println("result2 " + result2 * 4);
System.out.println("pi " + Math.PI);
}
结果是:
result1 3.141592653576877
result2 3.1415926529660116
pi 3.141592653589793
实际上,当 i
足够大时,您的第一个循环会在 (2 * i + 1)
的计算中出现 int
溢出,因此我不会依赖它的输出。
另一方面,第二个循环产生更正确的输出,因为 (2 * j + 1)
不会溢出,因为它执行 long
乘法。
This makes the "int - loop" calculates PI more accurate than "long - loop"
这可能只是巧合,因为 int
循环中的计算溢出了。
2 * i
当 i
接近循环结束时 会溢出最大值 int
即 2147483647.
使用 long 操作不会溢出。
正确的步骤是使用 long 类型。可能是因为一些奇怪的行为在正确的 PI 周围添加和删除了值,溢出暂时计算到更接近正确的 PI 的值。
我想改变几个值的for循环的限制会使最终结果与正确的PI相差更远。
因为你在
行溢出了
result1 += sign/(2 * i + 1);
其中 2*i
的值超过最大整数值
int 范围是 -2,147,483,648 to 2,147,483,647
,但是当您 2*i
以获得更大的值时,它会越过该范围。
最好坚持使用 long
,这会给你正确的输出。
您有整数溢出。
signed int 的最大容量为 (2^31)-1,即 2,147,483,647。
(1,607,702,095 * 2) 为 3215404190,大于 2,147,483,647。
当您将 i 更改为 long 时,您会将 i 的容量增加到 (2^63)-1。
注意到每个人都在指出整数溢出问题,但您可能需要一个解决方案。 (如果你已经有了,请忽略以下内容:) )
在代码的 (2 * i + 1)
部分出现溢出,您应该将 for 循环中的 i
最大为 (Integer.MAX_VALUE / 2 - 1)
,这将导致:
for (int i = 0; i <= (Integer.MAX_VALUE / 2 - 1); i++) {
result1 += sign/(2 * i + 1);
sign *= -1;
}
您也可以使用 (Long.MAX_VALUE / 2 - 1)
对较长的部分执行此操作,但它将 运行 持续很长时间。
我写了一个用莱布尼茨公式计算圆周率的程序:
[
我写了一个初始化类型为 "int" 的 for 循环,循环工作正常,但是当我将初始化类型更改为 "long" 时,结果发生了变化。只有当循环次数超过 10 亿次时才会发生这种情况。这使得 "int - loop" 计算 PI 比 "long - loop" 更准确。我不知道为什么会这样。请帮助我理解这个问题。谢谢!这是我的代码。
public static void main(String[] args) {
double result1 = 0;
double result2 = 0;
double sign = 1;
for (int i = 0; i <= 1607702095; i++) {
result1 += sign/(2 * i + 1);
sign *= -1;
}
sign = 1;
for (long j = 0; j <= 1607702095; j++) {
result2 += sign/(2 * j + 1);
sign *= -1;
}
System.out.println("result1 " + result1 * 4);
System.out.println("result2 " + result2 * 4);
System.out.println("pi " + Math.PI);
}
结果是:
result1 3.141592653576877
result2 3.1415926529660116
pi 3.141592653589793
实际上,当 i
足够大时,您的第一个循环会在 (2 * i + 1)
的计算中出现 int
溢出,因此我不会依赖它的输出。
另一方面,第二个循环产生更正确的输出,因为 (2 * j + 1)
不会溢出,因为它执行 long
乘法。
This makes the "int - loop" calculates PI more accurate than "long - loop"
这可能只是巧合,因为 int
循环中的计算溢出了。
2 * i
当 i
接近循环结束时 会溢出最大值 int
即 2147483647.
使用 long 操作不会溢出。
正确的步骤是使用 long 类型。可能是因为一些奇怪的行为在正确的 PI 周围添加和删除了值,溢出暂时计算到更接近正确的 PI 的值。
我想改变几个值的for循环的限制会使最终结果与正确的PI相差更远。
因为你在
行溢出了result1 += sign/(2 * i + 1);
其中 2*i
的值超过最大整数值
int 范围是 -2,147,483,648 to 2,147,483,647
,但是当您 2*i
以获得更大的值时,它会越过该范围。
最好坚持使用 long
,这会给你正确的输出。
您有整数溢出。
signed int 的最大容量为 (2^31)-1,即 2,147,483,647。
(1,607,702,095 * 2) 为 3215404190,大于 2,147,483,647。
当您将 i 更改为 long 时,您会将 i 的容量增加到 (2^63)-1。
注意到每个人都在指出整数溢出问题,但您可能需要一个解决方案。 (如果你已经有了,请忽略以下内容:) )
在代码的 (2 * i + 1)
部分出现溢出,您应该将 for 循环中的 i
最大为 (Integer.MAX_VALUE / 2 - 1)
,这将导致:
for (int i = 0; i <= (Integer.MAX_VALUE / 2 - 1); i++) {
result1 += sign/(2 * i + 1);
sign *= -1;
}
您也可以使用 (Long.MAX_VALUE / 2 - 1)
对较长的部分执行此操作,但它将 运行 持续很长时间。