簡體   English   中英

將有符號 32 位整數與無符號 64 位整數相加

[英]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 intsignen 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM