2013-08-30 109 views
0

該程序的每行輸出等於2^i-2,除了最後一行,等於2^64-1。爲什麼?爲什麼我需要從1 << 64減去2以得到2^64-1?

#include <stdio.h> 
#include <stdlib.h> 
#include <inttypes.h> 

int main(void) { 
    unsigned long long ONE = 1; 
    unsigned long long i; 
    for (i = 1; i <= 64; i++) { 
     printf("%"PRIu64"\n", (ONE << i) - 2); 
    } 

    return EXIT_SUCCESS; 
} 

輸出:

0 
2 
6 
14 
30 
62 
126 
254 
510 
1022 
2046 
... 
4611686018427387902 
9223372036854775806 
18446744073709551615 
+2

我非常確定移動64位導致未定義的行爲。你是否嘗試過總共移動64位,但分多步? –

+0

'unsigned long long i << = 63;我<< = 1;'無效簽名long long我= 64;'不 - 你說得對,謝謝。你知道我在哪裏可以閱讀更多關於此? – Kiwi

+0

答案已經鏈接到有用的資源,所以我不打擾更多地混淆這個問題。 –

回答

4

假設你ULL類型是64位寬,你進入未定義的行爲領域。按照C11 6.5.7 Bitwise shift operators

如果右操作數的值是負的或爲大於或等於所述推動左操作數的寬度行爲是未定義的。

什麼可能發生的是,偏移值被減小模64,和64 % 64爲零。因此,它只是評估ONE - 2這是包裝到264-1。但是,誠實地說,它可能僅僅是因爲UB沒有實際的限制而拔除:-)

5

你留下(你的機器上unsigned long long)轉向64 64位的,這是不確定的行爲。

順便說一句,unsigned long long ONE = 1;是不好的編碼風格,你可以簡單地使用1ULL

C11§6.5.7按位的移位運算符

整數優惠在每個操作數的執行。結果的類型是 升級左操作數的類型。 如果右操作數的值爲負或大於或等於提升左操作數的寬度,則行爲未定義。

+1

糟糕的編碼風格?這可能是我讀過的最糟糕的一行代碼!從字符到有意義的內容比例,其約** 26:1 ** – abelenky

+2

@abelenky嘿,不要責怪那些沒有設計語言關鍵詞的人。 –

+0

它可能是不好的風格,但它工作正常,整數推廣規則使它非常好的定義,'ULL' _could_應被視爲不必要的代碼阻塞。我更擔心一個名爲'ONE'的變量,特別是當你必須將它的值改爲'2'時:-) – paxdiablo

0

您達到了64bit(long long)的位限制。顯然,2^63左移會導致翻轉,導致2^0 = 1.

1 - 2 = -1當被視爲無符號(假設long long)爲2^64-1時。

這個結果很有意思,因爲左側的非翻轉實現會導致0,而不是1,這會導致2^64-2的減法運算。

相關問題