为什么循环中的foo + = foo + 1导致-1?

时间:2014-10-27 23:19:19

标签: java

在审查考试时,我注意到我写了一个逻辑错误,我认为这是因为复合赋值+ =因为Increment ++按预期执行但只有在将foo的值赋给foo +1时才会发生或

foo += foo + 1;

这是代码。

//Break Statement
    Boolean exit = false;
    int foo = 1, bar = 60;
    while (!exit) {         
        foo+=foo+1; //Bad Code
        //foo++; //Good Code
        //foo=foo+1; // Good Code
        //foo+=1; // Good Code
        //System.out.println(foo); //Results in -1 (Infinite Loop)
        if (foo == bar) {
            break;
        }
        System.out.println("stuff");
    }

我的问题是为什么foo + = foo + 1导致-1?

请注意:我是Stackoverflow的新手,无法对你的答案进行投票,所以我知道我感谢您的帮助,并提前感谢您!

3 个答案:

答案 0 :(得分:13)

如果你在谈论它在循环结束时为-1;这只是因为int已经签名并且它已经包装好了。

int foo = 1;
while (foo >= 0) {
    foo += foo + 1;
}
System.out.println(foo);

output -1。你可以trace through it

foo += 1 + 1 ---> 3
foo += 3 + 1 ---> 7
foo += 7 + 1 ---> 15
foo += 15 + 1 ---> 31
foo += 31 + 1 ---> 63
foo += 63 + 1 ---> 127
foo += 127 + 1 ---> 255
foo += 255 + 1 ---> 511
foo += 511 + 1 ---> 1023
foo += 1023 + 1 ---> 2047
foo += 2047 + 1 ---> 4095
foo += 4095 + 1 ---> 8191
foo += 8191 + 1 ---> 16383
foo += 16383 + 1 ---> 32767
foo += 32767 + 1 ---> 65535
foo += 65535 + 1 ---> 131071
foo += 131071 + 1 ---> 262143
foo += 262143 + 1 ---> 524287
foo += 524287 + 1 ---> 1048575
foo += 1048575 + 1 ---> 2097151
foo += 2097151 + 1 ---> 4194303
foo += 4194303 + 1 ---> 8388607
foo += 8388607 + 1 ---> 16777215
foo += 16777215 + 1 ---> 33554431
foo += 33554431 + 1 ---> 67108863
foo += 67108863 + 1 ---> 134217727
foo += 134217727 + 1 ---> 268435455
foo += 268435455 + 1 ---> 536870911
foo += 536870911 + 1 ---> 1073741823
foo += 1073741823 + 1 ---> 2147483647
foo += 2147483647 + 1 ---> -1

数学运算就是这样。每次迭代都会产生比2的幂小1的值。我想如果你算出代数,你就可以证明这一点。因此有意义的是它会达到-1,在32位带符号的int中,它是2 32 -1。

然后,当tomTomáš Zíma精明地指出时,它会卡住,因为-1 + -1 + 1仍然只是-1

另请注意,正如汤姆在下面的评论中发现的那样,无论您从哪个数字开始,您都会达到-1。这是因为foo += foo + 1foo = 2 * foo + 1相同,实际上只是foo = (foo << 1) | 1(左移[导致低位0]然后打开低位 - 与数字时加1相同甚至)。所以无论你从什么开始,在最多32次(或者有多少位)迭代之后,你最终都会将你的起始值从左侧移开并用全部1替换它(以2的补码代替) signed int,是值-1)。例如。带有签名的8位数:

abcdefgh         starting foo, 8 unknown bits
bcdefgh0         add to itself (or multiply by two, or left shift)
bcdefgh1         add one
...
cdefgh11         again
defgh111         and again
efgh1111         and again
fgh11111         and again
gh111111         and again
h1111111         and again
11111111         and again, now it's -1
... and for completeness:
11111110         left shift -1
11111111         add 1, it's back to -1

顺便说一句,无论你开始的是什么号码,你都不会达到60(或任何偶数,就此而言)。你可以用一些快速代数来显示:如果 60 = 2 * foo + 1 ,那么之前的 foo = 59/2 已经不是整数了;所以你永远不会有一个整数 foo ,这样 60 = 2 * foo + 1

答案 1 :(得分:7)

foo += foo + 1

被解析为

foo = (foo) + (foo + 1)

不是foo = (foo + 1)

意思是你得到

1: 3 (+ 1 + 2)
2: 7 (+3 + 4)
3: 15 (+7 +8)
4: 31 (+15 +16)
5: 63 (+32 +33)

所以你永远不会有foo == 60和无限循环。

eta:我似乎只是自己学到了一些东西。 int翻转并命中-1。谢谢@Jason C

答案 2 :(得分:3)

请考虑以下代码:

public class Test {
    public static void main(String[] args) {
        int foo = 1;
        while (true) {
            foo += foo + 1;
            System.out.println(foo);
        }
    }
}

经过几次迭代后,它会一遍又一遍地打印-1。原因如下:溢出!只要foo的值等于2147483647,结果就是-1。被评估的表达式如下:

2147483647 + 2147483647 + 1 // which is -1, given you work with int

一旦你得到-1,它将永远不会再改变这个值。为什么呢?

foo += foo + 1

基本相同
foo = foo + foo + 1

,foo = -1,相当于:

foo = -1 + -1 + 1

你的循环永远不会结束的原因是你使用的条件。 Foo永远不会等于60.这就是重点。