[英]Sum signed 32-bit int with unsigned 64bit int
在我的應用程序中,我收到兩個signed 32-bit int
,我必須存儲它們。 我必須創建一種計數器,我不知道它什么時候會被重置,但我會經常收到大值。 因此,為了存儲這些值,我決定使用兩個unsigned 64-bit int
。
以下可能是計數器的簡單版本。
struct Counter
{
unsigned int elementNr;
unsigned __int64 totalLen1;
unsigned __int64 totalLen2;
void UpdateCounter(int len1, int len2)
{
if(len1 > 0 && len2 > 0)
{
++elementNr;
totalLen1 += len1;
totalLen2 += len2;
}
}
}
我知道如果將較小的類型轉換為較大的類型(例如 int 到 long),則應該沒有問題。 但是,同時從 32 位 rappresentation 傳遞到 64 位 rappresentation 以及從有符號到無符號,對我來說是新事物。
仔細閱讀,我知道len1
應該從 32 位擴展到 64 位,然后應用符號擴展。 因為unsigned int
和signen int
具有相同的等級( 第 4.13 節),所以應該轉換后者。
如果len1
存儲一個負值,從有符號傳遞到無符號將返回錯誤值,這就是為什么我檢查 function 開頭的正數。 但是,對於積極的價值觀,我認為應該沒有問題。
為了清楚起見,我可以像這樣重寫UpdateCounter(int len1, int len2)
void UpdateCounter(int len1, int len2)
{
if(len1 > 0 && len2 > 0)
{
++elementNr;
__int64 tmp = len1;
totalLen1 += static_cast<unsigned __int64>(tmp);
tmp = len2;
totalLen2 += static_cast<unsigned __int64>(tmp);
}
}
可能有一些我沒有考慮過的副作用。 還有其他更好更安全的方法嗎?
一點背景知識,僅供參考:二元運算符(例如算術加法)適用於相同類型的操作數(轉換為的特定 CPU 指令取決於兩個指令操作數必須相同的數字表示)。 當你寫這樣的東西時(使用固定寬度 integer 類型是明確的):
int32_t a = <some value>;
uint64_t sum = 0;
sum += a;
如您所知,這涉及隱式轉換,更具體地說,是根據integer 轉換等級進行的整體提升。 所以表達式sum += a;
相當於sum += static_cast<uint64_t>(a);
,因此a
被提升為具有較低等級。 讓我們看看這個例子中發生了什么:
int32_t a = 60;
uint64_t sum = 100;
sum += static_cast<uint64_t>(a);
std::cout << "a=" << static_cast<uint64_t>(a) << " sum=" << sum << '\n';
output 是:
a=60 sum=160
所以一切都如預期的那樣好。 讓我們看看添加一個負數會發生什么:
int32_t a = -60;
uint64_t sum = 100;
sum += static_cast<uint64_t>(a);
std::cout << "a=" << static_cast<uint64_t>(a) << " sum=" << sum << '\n';
output 是:
a=18446744073709551556 sum=40
結果是預期的40
:這依賴於二進制補碼 integer 表示(注意:無符號 integer 溢出不是未定義的行為),當然,只要確保總和不會變為負數,一切都可以。
回到你的問題,如果你總是添加正數或者至少確保總和永遠不會是負數,你不會有任何驚喜......直到你達到最大可表示值std::numeric_limits<uint64_t>::max()
(2^64-1 = 18446744073709551615 ~ 1.8E19)。 如果您遲早繼續無限期地添加數字,您將達到該限制(這也適用於您的計數器elementNr
)。 您將通過每毫秒添加 2^31-1 (2147483647) 大約三個月來溢出 64 位無符號 integer,因此在這種情況下,建議檢查:
#include <limits>
//...
void UpdateCounter(const int32_t len1, const int32_t len2)
{
if( len1>0 )
{
if( static_cast<decltype(totalLen1)>(len1) <= std::numeric_limits<decltype(totalLen1)>::max()-totalLen1 )
{
totalLen1 += len1;
}
else
{// Would overflow!!
// Do something
}
}
}
當我必須累積數字並且我對准確性沒有特別要求時,我經常使用double
精度,因為最大可表示值非常高( std::numeric_limits<double>::max()
1.79769E+308
)並且達到溢出我需要每皮秒添加 2^32-1=4294967295 1E+279 年。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.