![](/img/trans.png)
[英]When targeting 64-bit platforms in C, is it better to use 64-bit variables for array references?
[英]Declaring 64-bit variables in C
我有個問題。
uint64_t var = 1; // this is 000000...00001 right?
在我的代碼中,這有效:
var ^ (1 << 43)
但它怎么知道1應該是64位? 我不應該寫這個嗎?
var ^ ( (uint64_t) 1 << 43 )
正如你所想的那樣,1是一個普通的有符號int
(在你的平台上可能是2位補碼算法中的32位寬),所以是43,所以1<<43
導致溢出:事實上,如果兩個參數都是類型為int
運算符規則表明結果也是int
。
仍然,在C簽名整數溢出是未定義的行為,所以在原則上任何事情都可能發生。 在你的情況下,編譯器可能會發出代碼來在64位寄存器中執行該移位,所以幸運的是它似乎可行; 要獲得保證正確的結果,您應該使用您編寫的第二種形式,或者,替代地,使用ull
后綴將unsigned long long
文本指定為1
( unsigned long long
保證至少為 64位)。
var ^ ( 1ULL << 43 )
我推薦OP的方法,施放常數( (uint64_t) 1 << 43 )
對於OP的小例子,下面的2 可能會執行相同的操作。
uint64_t var = 1;
// OP solution)
var ^ ( (uint64_t) 1 << 43 )
// Others suggested answer
var ^ ( 1ULL << 43 )
以上結果具有相同的值 ,但是類型不同 。 潛在的區別在於C中有兩種類型: uint64_t
和unsigned long long
以及可能跟隨的內容。
uint64_t
的精確范圍為0到2 64 -1。
unsigned long long
的范圍為0到至少為 2 64 -1。
如果unsigned long long
總是 64位,因為它似乎在很多機器上都存在,沒有問題,但讓我們展望未來,並說這段代碼是在unsigned long long
為16字節的機器上運行的(0到至少 2 128 -1)。
下面一個人為的例子:的第一個結果^
是一個uint64_t
,當乘以3,產品仍會被uint64_t
,執行模2 64,應該發生溢出 ,則該結果被分配到d1
。 在下一種情況下, ^
的結果是unsigned long long
,當乘以3時,乘積可能大於2 64 ,然后將其分配給d2
。 所以d1
和d2
有不同的答案。
double d1, d2;
d1 = 3*(var ^ ( (uint64_t) 1 << 43 ));
d2 = 3*(var ^ ( 1ULL << 43 ));
如果想要使用unit64_t
,請保持一致。 不要假設unit64_t
和unsigned long long
是相同的。 如果你的回答是unsigned long long
,那很好。 但根據我的經驗,如果開始使用固定大小的類型,如uint64_t
,則不希望變體大小類型弄亂計算。
var ^ ( 1ULL << 43 )
應該這樣做。
使unit64_t
常量的可移植方法是使用UINT64_C
宏(來自stdint.h
):
UINT64_C(1) << 43
很可能UINT64_C(c)
被定義為類似c ## ULL
東西。
從C標准:
宏
INT
N_C(value)
將擴展為對應於int_least
N_t
類型的整數常量表達式。 宏UINTN_
C(value)
將擴展為對應於類型uint_least
N_t
的整數常量表達式。 例如,如果uint_least64_t
是unsigned long long int
類型的名稱,則UINT64_C(0x123)
可能會擴展為整數常量0x123ULL
。
您的編譯器不知道轉換應該以64位完成。 但是,對於此特定代碼的特定配置中的這個特定版本的編譯器,兩個錯誤恰好是正確的。 不要指望它。
假設int
在您的平台上是32位類型(很可能), 1 << 43
中的兩個錯誤是:
x
是類型的int
或unsigned int
,然后x << 43
有未定義的行為,如同x << 32
或任何其他x << n
其中n≥32。例如1u << 43
將具有未定義行為太。 0x12345 << 16
具有未定義的行為,因為左操作數的類型是有符號類型int
但結果值不適合int
。 另一方面, 0x12345u << 16
是明確定義的,其值為0x23450000u
。 “未定義的行為”意味着編譯器可以自由地生成崩潰或返回錯誤結果的代碼。 碰巧在這種情況下你獲得了理想的結果 - 這不是禁止的,但墨菲的法則規定有一天生成的代碼不會做你想要的。
為了保證操作發生在64位類型上,您需要確保左操作數是64位類型 - 您分配結果的變量類型無關緊要。 這與float x = 1 / 2
問題相同,導致x
包含0而不是0.5:只有操作數的類型才能確定算術運算符的行為。 任何(uint64)1 << 43
或(long long)1 << 43
或(unsigned long long)1 << 43
1ll << 43
或1ull << 43
或1ull << 43
都可以。 如果使用帶符號類型,則僅在沒有溢出時定義行為,因此如果您希望在溢出時截斷,請確保使用無符號類型。 通常建議使用無符號類型,即使由於行為是可重現的而不應發生溢出 - 如果使用帶符號類型,那么僅僅為了調試目的而打印輸出值的行為可能會改變行為(因為編譯器喜歡利用未定義的行為生成任何代碼在微觀層面上最有效,這對諸如寄存器分配壓力之類的事情非常敏感。
由於您希望結果為uint64_t
類型,因此使用該類型執行所有計算會更清楚。 從而:
uint64_t var = 1;
… var ^ ((uint64_t)1 << 43) …
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.