繁体   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