2012-12-11 86 views
4

假設我有以下兩種類型:用簽名long long結果減去無符號長整數?

typedef unsigned long long uint64; 
typedef signed long long sint64; 

而且我有這些變量:

uint64 a = ...; 
uint64 b = ...; 
sint64 c; 

我希望從減去B和結果分配給C,顯然,如果的絕對值差別大於2^63比它會包裹(或未定義),這是可以的。但對於絕對差值小於2^63的情況,我希望結果是正確的。

以下三種方式:

c = a - b; // sign conversion warning ignored 

c = sint64(a - b); 

c = sint64(a) - sint64(b); 

哪他們都保證通過該標準的工作? (以及爲什麼/如何?)

+0

又見此相關的問題:如何促銷規則時,在二元運算符兩邊的符號不一致工作?](http://stackoverflow.com/q/6770258/636019) – ildjarn

回答

4

這三個工作都沒有。如果差值爲負數(無論絕對值),第一個失敗,第二個與第一個相同,如果任一操作數太大,第三個失敗。

沒有分支就不可能實現。

c = b < a? a - b : - static_cast<sint64>(b - a); 

從根本上說,unsigned類型使用模運算,沒有任何形式的符號位。他們不知道他們纏繞在一起,而語言規範並不能識別負數。另外,分配一個超出有符號整型變量範圍的值將導致實現定義的潛在無意義結果(整數溢出)。

考慮一臺沒有硬件的機器在原生負整數和二進制補碼之間進行轉換。儘管如此,它可以使用按位取反和本地二進制補碼來執行二進制補碼減法。 (也許是奇怪的,但這就是C和C++當前所要求的。)語言將它留給程序員,然後轉換爲負值。要做到這一點的唯一方法是否定一個正值,這要求計算出的差值是正值。所以...

最好的解決方案是避免任何嘗試代表負數作爲一個大的正數在首位。

編輯:我以前忘了演員,這將產生一個很大的無符號值,等同於其他解決方案!

0

如果差值的絕對值大於2^63比 會換行(或者說是未定義)這就沒關係。但對於絕對差值小於2^63的情況,我希望結果是正確的。

然後,假設是傳統架構,那麼您建議的所有三種符號都可以工作。 的顯着差異是第三個sint64(a) - sint64(b)調用未定義的行爲 時,差異是不可表示的,而前兩個是 保證環繞(無符號算術溢出保證環繞和無符號轉換爲signed是實現定義,而有符號算術溢出未定義)。

+0

三個工作中沒有一個。如果差值爲負數(無論絕對值),第一個失敗,第二個與第一個相同,如果任一操作數太大,第三個失敗。 – Potatoswatter

+0

@Patatoswatter我們必須以不同的方式理解這個問題。我想問的是計算'a'和'b'之間的差異,而不是差異的絕對值。如果這是目標,那麼'sint64(a - b)'即使無符號減法'a - b'環繞。 –

+0

這就是問題所在。鑑於絕對值可以用帶符號的類型(包括負值)表示,目標是可靠地計算它。 – Potatoswatter

1

Potatoswatter的回答可能是最實用的解決方案,但「沒有分支就無法實施」就像是一塊紅色的碎布給我。如果你的假設系統實現了這樣的未定義的溢出/拋出操作,假想系統通過殺死小狗來實現分支。

所以我不完全熟悉什麼標準(S)會說,但這個怎麼樣:

sint64 c,d,r; 
c = a >> 1; 
d = b >> 1; 
r = (c-d) * 2; 
c = a & 1; 
d = b & 1; 
r += c - d; 

我寫它在一個相當冗長FASION所以個人業務是明確的,但留下了一些隱含的演員。有什麼沒有定義?

Steve Jessop正確地指出,在差值恰好爲2^63-1的情況下,這是失敗的,因爲減去1之前的乘法溢出。

所以這裏是一個甚至醜陋版本,它應該涵蓋所有溢/上溢條件:

sint64 c,d,r,ov; 
c = a >> 1; 
d = b >> 1; 
ov = a >> 63; 
r = (c-d-ov) * 2; 
c = a & 1; 
d = b & 1; 
r += ov + ov + c - d; 
+0

我比C++更熟悉C,但是在C中,負數的左邊是未定義的(C99-C11)或可以說是實現定義的(C90)。將有符號數字左移大到超過該類型的範圍也是未定義的(這是您的代碼避免的)。 –

+0

是的,我想這可能是不確定的轉移到符號位,但我找不到任何明確的參考。 – JasonD

+0

如果前者並非總是以C++定義,則可以隨時將'<< 1'更改爲'* 2'。 –

相關問題