我有一个简单的程序:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
当我运行此程序时,我在输出中看到0
的{{1}}。我原本预计第一次会有i
,然后是i = 1 + 1
,然后是i = 2 + 2
等。
这是因为我们尝试在左侧重新声明i = 4 + 4
时,其值会重置为i
吗?
如果有人能指出我的精细细节,那就太棒了。
将0
更改为int
,似乎是按预期打印数字。我对它达到最大32位值的速度感到惊讶!
答案 0 :(得分:334)
问题是整数溢出。如果它溢出,它会回到最小值并从那里继续。如果它下溢,它会回到最大值并从那里继续。下图是里程表。我用它来解释溢出。这是一个机械溢出,但仍然是一个很好的例子。
在里程表中,max digit = 9
,超出最大值意味着9 + 1
,其结束并给出0
;但是,没有更高的数字可以更改为1
,因此计数器会重置为zero
。你明白了 - 现在想到“整数溢出”。
int类型的最大十进制文字是2147483647(2 31 -1)。所有 从0到2147483647的十进制文字可能出现在int的任何地方 文字可能会出现,但文字2147483648可能只出现 一元否定算子的操作数 - 。
如果整数加法溢出,则结果为低位 数学和的位用一些足够大的数字表示 二进制补码格式。如果发生溢出,则表示符号 结果与两者的数学和的符号不同 操作数值。
因此,2147483647 + 1
溢出并包裹到-2147483648
。因此int i=2147483647 + 1
将溢出,这不等于2147483648
。另外,你说“它总是打印0”。它没有,因为 http://ideone.com/WHrQIW 。下面,这8个数字表示它转动和溢出的点。然后它开始打印0。此外,不要惊讶它的计算速度有多快,今天的机器很快。
268435456
536870912
1073741824
-2147483648
0
0
0
0
答案 1 :(得分:168)
问题是整数溢出造成的。
在32位二进制补码算法中:
i
确实开始具有两个幂的值,但是一旦你到达2 30 ,溢出行为就开始了:
2 30 + 2 30 = -2 31
-2 31 + -2 31 = 0
...在int
算术中。
答案 2 :(得分:46)
不,它不会只打印零。
将其更改为此,您将看到会发生什么。
int k = 50;
while (true){
i = i + i;
System.out.println(i);
k--;
if (k<0) break;
}
发生的事情称为溢出。
答案 3 :(得分:15)
static int i = 1;
public static void main(String[] args) throws InterruptedException {
while (true){
i = i + i;
System.out.println(i);
Thread.sleep(100);
}
}
out put:
2
4
8
16
32
64
...
1073741824
-2147483648
0
0
when sum > Integer.MAX_INT then assign i = 0;
答案 4 :(得分:4)
由于我没有足够的声誉,我无法在带有受控输出的C中发布相同程序的输出图片,你可以尝试自己看看它实际打印32次然后由于溢出而解释 i = 1073741824 + 1073741824 更改为 -2147483648 还有一个进一步的加法超出了int的范围并变为 Zero 。
#include<stdio.h>
#include<conio.h>
int main()
{
static int i = 1;
while (true){
i = i + i;
printf("\n%d",i);
_getch();
}
return 0;
}
答案 5 :(得分:4)
使用固定数量的二进制数字将i
的值存储在内存中。当一个数字需要的数字多于可用数字时,只存储最低位数(最高位数会丢失)。
将i
添加到自身与将i
乘以2相同。就像将十进制数字乘以10一样,可以通过将每个数字向左滑动并在右边放置零来执行,以二进制表示法将数字乘以2可以以相同的方式执行。这会在右侧添加一位数字,因此数字会在左侧丢失。
此处起始值为1,因此如果我们使用8位数来存储i
(例如),
00000001
00000010
00000100
等等,直到最后的非零步骤
10000000
00000000
无论分配多少二进制数来存储数字,无论起始值是多少,最终所有数字都会丢失,因为它们被推到左边。在那之后,继续加倍数字将不会改变数字 - 它仍将由全零表示。
答案 6 :(得分:3)
这是正确的,但经过31次迭代后,1073741824 + 1073741824无法正确计算,之后只打印0。
你可以重构使用BigInteger,这样你的无限循环就能正常工作。
public class Mathz {
static BigInteger i = new BigInteger("1");
public static void main(String[] args) {
while (true){
i = i.add(i);
System.out.println(i);
}
}
}
答案 7 :(得分:2)
为了调试这种情况,最好减少循环中的迭代次数。使用此代替while(true)
:
for(int r = 0; r<100; r++)
然后,您可以看到它以2开头并将值加倍,直到它导致溢出。
答案 8 :(得分:2)
我将使用8位数字进行说明,因为它可以在短空间内完全详细说明。十六进制数字以0x开头,而二进制数字以0b开头。
8位无符号整数的最大值为255(0xFF或0b11111111)。 如果添加1,通常会得到:256(0x100或0b100000000)。 但由于那个位数太多(9),超过了最大值,所以第一部分就会被删除,让你有效地为0(0x(1)00或0b(1)00000000,但是丢弃了1)。
所以当你的程序运行时,你会得到:
1 = 0x01 = 0b1
2 = 0x02 = 0b10
4 = 0x04 = 0b100
8 = 0x08 = 0b1000
16 = 0x10 = 0b10000
32 = 0x20 = 0b100000
64 = 0x40 = 0b1000000
128 = 0x80 = 0b10000000
256 = 0x00 = 0b00000000 (wraps to 0)
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
...
答案 9 :(得分:1)
类型int
的最大小数字是 2147483648 (= 2 31 )。 0到2147483647 的所有小数文字都可能出现在int文字可能出现的任何地方,但文字 2147483648 可能只出现在一元否定运算符的操作数上。
如果整数加法溢出,则结果是数学和的低阶位,如某些足够大的二进制补码格式所示。如果发生溢出,则结果的符号与两个操作数值的数学和的符号不同。