2014-10-27 61 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.這就是要點。

相關問題