[英]Interpreting an unsigned (long) int as signed in C++
我想將unsigned long
的 32 位重新解釋為signed long
。 完全相同的位,只是被視為 2 的補碼整數而不是無符號整數。 我不認為簡單地將它投射到long
就能解決問題。 我錯了嗎?
或者也許有更好的方法。 我使用 unsigned long 作為計時器。 有時我會讀取它的當前值並將其與之前的讀數(都是unsigned long
s)進行比較,以查看已經過去了多少時間。 我需要處理可能的溢出,這會導致當前值小於前一個值。 將兩個值解釋為帶符號的 long 並減去似乎給出了正確的答案。
我試過這個:
return reinterpret_cast<long>(time4) - reinterpret_cast<long>(currTimeLo); // treat unsigned as 2's complement
但剛剛得到一個編譯器錯誤:
Arduino: 1.6.7 (Mac OS X), Board: "Arduino Nano, ATmega328"
invalid cast from type 'long unsigned int' to type 'long int'
關於通過比較兩個無符號計數器來檢查已經過去了多少時間的更深層次/原始問題,其中可能有 1 次單循環:
使用無符號算術簡單地從最新的減去最早的。
假設您的currTimeLo
是當前時間的計數器值,而time4
是某個較早的值,並且它們都是無符號類型(或者有符號類型中的一個提升了另一個的無符號類型),
return currTimeLo - time4;
, where n is the number of bits in the value representation of the unsigned type.這是因為 C++ 保證以 2 為模執行無符號算術,其中n是無符號類型的值表示中的位數。
在有超過 1 次環繞的情況下,這種方法將不起作用。 在這種情況下,您需要使用具有更大數字范圍的類型。
關於將無符號值解釋為 2 的補符號值的問題標題的問題:
首先請注意,這是沒有必要的。 這是 X/Y 問題中的 Y。 獲取兩個計數器之間的差異,其中最新的可能已包裝,是原始 X,它有一個簡單的解決方案(上圖)。
但是,由於這是標題中所問的內容:
據我所知,所有現存的 C++ 實現都是用於有符號整數使用 2 的補碼表示的體系結構。
當原始值不能用該類型表示時,Holy Standard™ 將它留給實現來定義轉換為有符號整數類型的結果。 任何合理的 C++ 實現都會讓您通過static_cast
做到這一點。 因此,
return static_cast<long>(time4) - static_cast<long>(currTimeLo);
但是不能保證您在 Arduino 中的編譯器在這方面是合理的。
您將必須檢查這一點,並在必要時使用相關選項,假設如果默認情況下不合理,則可以調整行為。
解決方法包括
通過reinterpret_cast
轉換指針或引用,
通過例如memcpy
復制字節,形式上安全但復雜且不必要地潛在低效,
使用正式的 UB 工會成員訪問權限,或
安全但復雜,將值拆分並重新組合。
最后一點可以以一種近乎優雅的方式完成,有人在之前在 SO 上針對此問題的語言律師變體發布了該內容。 不幸的是,我不記得那個技巧,只是它讓我印象深刻,因為它是如此明顯但我沒有想到。 但我推薦簡單的static_cast
,經過適當測試。
您想使用static_cast
,例如:
static_cast<signed long>(your_unsigned_long)
在 2 的補碼中,有符號和無符號之間的簡單轉換有效,因為它包裝了值模 2 n ,即對待與其他類型相同的位模式。 例如(long)0xFFFFFFFFu
返回 -1
然而,事實是加法和減法都會產生 1 個進位/借位。 額外的位必須與低 32 位一起存儲在某個位置。 因此,簡單地將值轉換為有signed
和減法並不適用於所有情況,盡管它適用於彼此相距不遠的值。 嘗試LONG_MAX - LONG_MIN
或LONG_MIN - LONG_MAX
怎么看的結果不能被保存在一個long
,即使兩個操作數都是long
小號
為了克服這個問題,唯一的方法是使用更廣泛的類型
return static_cast<long long>(time4) - static_cast<long long>(currTimeLo);
或手動處理 big int 算術
unsigned long timeDiff;
if (time4 > rcurrTimeLo) // time hasn't overflowed and time4 is later than rcurrTimeLo
{
timeDiff = time4 - rcurrTimeLo;
// do something, for example set overflow/later flag:
later = 1;
}
else
{
timeDiff = rcurrTimeLo - time4;
// set "earlier" flag
later = 0;
}
如果在函數中使用它,則必須同時返回溢出進位和低 32 位差異,因此第一種解決方案在 32 或 64 位計算機上似乎更容易,而第二種解決方案在 8 位計算機上會更快像 ATmega 一樣的位 MCU
如果您可以保證兩個操作數之間的距離永遠不會超過LONG_MAX
,那么簡單的static_cast
到 long 將起作用
return static_cast<long>(time4) - static_cast<long>(currTimeLo);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.