我想将unsigned long
的32位重新解释为signed long
。完全相同的位,仅被视为2的补码整数,而不是无符号整数。我不认为简单地将其投射到long
就行了。我错了吗?
或许还有更好的方法。我使用unsigned long作为计时器。偶尔我会读取它的当前值并将其与之前的读数(两个unsigned long
s)进行比较,以查看已经过了多长时间。我需要处理一个可能的溢出,这会导致当前值比前一个值少。将两个值解释为带符号的长数并减去似乎给出了正确的答案。
我试过了:
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'
答案 0 :(得分:2)
关于通过比较两个未签名计数器来检查已经过了多长时间的更深层/原始问题,其中可能有一个单一的回绕:
使用无符号算术简单地从最新的中减去。
假设您的currTimeLo
是当前时间的计数器值,time4
是某个较早的值,并且它们都是无符号类型(或者有符号类型的值会提升无符号类型)另一个),
return currTimeLo - time4;
这是因为C ++保证以2 n 模式执行无符号算术,其中 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联盟成员访问权限,或
安全但复杂,将值拆分并重新组合。
最后一点可以用一种近乎优雅的方式完成,有人在早些时候发布了对这个问题的语言律师变体的回复。不幸的是,我不记得那个伎俩,只是因为它让我印象深刻,而我却没有想到这一点。但我推荐简单static_cast
,经过适当测试。
答案 1 :(得分:0)
您想使用static_cast
,例如:
static_cast<signed long>(your_unsigned_long)
答案 2 :(得分:0)
在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);
或手动处理大型int算法
if (time4 > rcurrTimeLo) // time hasn't overflowed
{
timediff = time4 - rcurrTimeLo;
// do something, for example set overflow flag:
OV = 0;
}
else
{
timediff = rcurrTimeLo - time4;
// do something, for example set overflow flag:
OV = 1;
}
如果在函数中使用它,则必须同时返回溢出进位和低32位差异,因此第一种解决方案在32位或64位计算机上似乎更容易,而第二种解决方案在像ATmega这样的8位MCU
如果你可以保证2个操作数永远不会超过LONG_MAX
,那么一个简单的static_cast
到long将会起作用
return static_cast<long>(time4) - static_cast<long>(currTimeLo);