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