2014-10-27 79 views
9

在审查考试时,我注意到我写了一个逻辑错误,我相信这是因为复合赋值+ =,因为Increment ++按预期执行,但只有在将foo的值赋予foo +1或为什么循环中的foo + = foo + 1会导致-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和无法投票了你的答案,所以知道我感谢任何帮助,并提前感谢你!

+2

您可以通过单击答案旁边的复选标记来接受答案。 – 2014-10-27 23:20:42

+0

我看不到这会导致'-1'。第一次迭代应该是3。它可能是一个无限循环,因为它会跳过60的数字。但是可以通过说'if(foo> = bar)' – 2014-10-27 23:23:16

+1

来解决这个问题。我不能用你的代码在问题中重现这一点。你能解决问题到可重现的事情吗?通常你会发现这个过程已经可以回答你的问题。 – 2014-10-27 23:25:10

回答

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 

数学只是这样工作。每次迭代的结果都是1的值,小于2的幂。我猜如果你计算出代数,你可以显示出来。所以有意义的是,它会达到-1,这在32位有符号的int中是2 -1。

然后,由于tomTomáš Zíma巧妙地指出,它被卡住,因为-1 + -1 + 1仍然只是-1

还要注意,正如tom在下面的评论中发现的那样,无论你开始的是什么数字,你都会打-1。这是因为foo += foo + 1foo = 2 * foo + 1相同,这实际上只是foo = (foo << 1) | 1(左移[导致低位0],然后打开低位 - 与数字偶数时加1相同)。所以不管你从哪里开始,在最多32个迭代(或者是多个比特)迭代之后,你最终将你的起始值从左边移开,并用全1替换(这在二进制补码中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

+0

这教会了我一些东西。谢谢。 – tom 2014-10-27 23:28:20

+1

如果你以2(或任何其他数字)开始它,你仍然打-1。怪异的.http://ideone.com/jQIzb6 – tom 2014-10-27 23:41:39

+0

@tom哇。这*是有点怪异的。我需要关闭我的网络浏览器,所以我不会被卡住,试图找出这个数学上发生了什么...... 8-) – 2014-10-27 23:43:57

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

+0

由于foo可能永远不会相等吧,它可能是一个溢出错误?我从来没有经历过-1的算术序列结果。 – 2014-10-27 23:24:03

+0

堆栈溢出会导致堆栈空间不足,这通常发生在递归函数调用太深时。 OP的代码中没有消耗堆栈空间的东西。 – 2014-10-27 23:29:23

+0

你说得对。一旦它达到-1,它就停留在那里。 – tom 2014-10-27 23:30:55

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.这就是要点。

相关问题