2017-07-06 22 views
-1

編輯:我更新了示例爲C.我特別關心C而不是C++(對於混淆,請參閱下面的情況)。C轉換籤名爲無符號維持精確位

我正在尋找一種安全的方法來將有符號整數轉換爲無符號整數,同時始終保持轉換之間完全相同的位模式。據我所知,簡單地說,鑄造具有未定義的或依賴於實施的行爲,因此依靠它是不安全的(下面的情況A)。但是像OR這樣的按位運算符(下面的情況B)呢?可以按位或用於安全地將簽名轉換爲無符號?那反過來呢?

實施例:

#include <stdio.h> 

int main() { 
    // NOTE: assuming 32bit ints 
    // example bit pattern: 11111111110001110001001111011010 
    // signed int value: -3730470 
    // unsigned int value: 4291236826 

    // example 1 
    // signed -> unsigned 
    int s1 = -3730470; 
    unsigned int u1a = (unsigned int)s1; 
    unsigned int u1b = (unsigned int)0 | s1; 

    printf("%u\n%u\n", u1a, u1b); 

    // example 2 
    // unsigned -> signed 
    unsigned int u2 = 4291236826; 
    int s2a = (int)u2; 
    int s2b = (int)0 | u2; 

    printf("%i\n%i\n", s2a, s2b); 
} 

情況:我寫一個PostgreSQL C-Language function /分機來增加popcount功能(my first attempt code here)。 PostgreSQL不支持無符號類型(ref)。計算popcount的所有有效方法都要求無符號數據類型正常工作。因此,我必須能夠將簽名數據類型轉換爲無符號數據類型,而不改變位模式。

題外話:我也知道一個替代的解決辦法是使用PostgreSQL位串bitvarbit數據類型,而不是整數數據類型的,但我的目的是整型數據類型更容易使用和管理。

+0

使用「聯合」來保持精確的位。 「簡單地說,鑄造具有未定義或依賴於實現的行爲,因此依賴它並不安全」並非如此。 – chux

+2

代碼似乎是C++,但問題被標記爲C .... –

+1

請記住,有符號位表示是實現定義的。所以顯然它會在不同的平臺上表現不同。 –

回答

2

安全的方式爲有符號整數轉換爲無符號整數,同時始終保持轉換

一個union將如下工作之間的確切相同的位模式,即使int是一種罕見的非-2補充。只有在非常期待的平臺上(在硅片墓地裏滴答滴答),INT_MAX == UINT_MAX這會成爲一個問題。

union { 
    int i; 
    unsigned u; 
} x = { some_int }; 
printf("%d\n", some_int); 
printf("%u\n", x.u); 

然而,如果人們可以限制自己對常見的補int,以下就足夠了。

unsigned u = (unsigned) some_int; 

但怎麼樣(以下情況B)像位運算符OR?
可以按位或用於安全地將帶符號轉換爲無符號?

以下|就像是一個隱藏的鑄造由於整數優惠

如果int可以表示原始類型的所有的值(由寬度爲受限制,對於一個位字段),將該值轉換爲int;否則,它將轉換爲unsigned int。 C11dr§6.3.1。1 3

int s1 = -3730470; 
unsigned int u1b = (unsigned int)0 | s1; 
// just like 
       = (unsigned int)0 | (unsigned int)s1; 
       =     (unsigned int)s1; 

什麼反?

轉換一個unsigned intsigned int如果該值是在兩個[0...INT_MAX]表示的被很好地定義。轉換的失int -range unsignedint是...

任一結果是實現定義或實現定義的信號上升。 §6.3.1.33

最好使用無符號類型進行位操作。
下面的代碼可能經常按照希望工作,但不應該用於強健的編碼。

// NOTE: assuming 32bit ints, etc. 
unsigned int u2 = 4291236826; 
int s2a = (int)u2; // avoid this 

替代

int s2a; 
if (u2 > INT_MAX) { 
    // Handle with some other code 
} else { 
    s2a = (int) u2; // OK 
} 

BTW:更好地追加u無符號常量像4291236826傳達給那的確是一個無符號常量旨在編譯器,而不是一個long long像4291236826.

unsigned int u2 = 4291236826u; 
+0

聽起來像'int s1 = -3730470; unsigned int u1a =(unsigned int)s1;'將爲我的目的工作。謝謝! – Mike

+0

是的 - 去無符號類型幾乎沒有困難。最好避免去'int'。 – chux

+0

太棒了。其實,明確演員是必要的嗎?我假設我可以做'unsigned int u1a = s1;'? – Mike