在Arduino C:
我想重新解释无符号长整数的32位作为有符号长整数 . 完全相同的位,仅被视为2的补码整数,而不是无符号整数 . 我不认为简单地将它投射到(长)就可以了 . 我错了吗?
或许还有更好的方法 . 我使用unsigned long作为计时器 . 偶尔我会读取它的当前值并将其与之前的读数(两个无符号长度)进行比较,以查看已经过了多长时间 . 我需要处理一个可能的溢出,这会导致当前值比前一个值少 . 将两个值解释为带符号的长数并减去似乎给出了正确的答案 .
我试过这个:
return reinterpret_cast<long>(time4) - reinterpret_cast<long>(currTimeLo); // treat unsigned as 2's complement
但只是有一个编译器错误:
Arduino:1.6.7(Mac OS X),主板:“Arduino Nano,ATmega328”从类型'long unsigned int'无效转换为'long int'类型
3 回答
关于通过比较两个未签名计数器来检查已经过了多少时间的更深层/原始问题,其中可能有一个单一的环绕:
简单地使用无符号算术从最新的中减去最早的 .
假设你的
currTimeLo
是当前时间的计数器值,并且time4
是某个较早的值,并且它们都是无符号类型(或者有符号类型的那个提升了另一个的无符号类型),这是因为C保证以2n为模执行无符号算术,其中n是无符号类型的值表示中的位数 .
如果有超过1个回绕,这种方法将无效 . 在这种情况下,您需要使用数字范围较大的类型 .
关于问题 Headers 将无符号值解释为2的补码有符号值的问题:
首先请注意,没有必要 . 它是X / Y问题中的Y.获得最新可能包含的两个计数器之间的差异是原始X,它有一个简单的解决方案(上图) .
但是,因为这是 Headers 中的问题:
据我所知,所有现存的C实现都是针对有符号整数使用2的补码表示的体系结构 .
当原始值无法在该类型中表示时,Holy Standard™将其留给实现来定义转换为有符号整数类型的结果 . 任何合理的C实现都只是让你通过
static_cast
来实现 . 因此,But 无法保证Arduino中的编译器在此方面是合理的 .
您必须检查,并在必要时使用相关选项,假设默认情况下如果行为不合理则可以调整行为 .
解决方法包括
通过
reinterpret_cast
转换指针或引用,通过例如复制字节
memcpy
,正式安全但复杂且不必要的潜在低效,使用正式的UB联盟成员访问,或
安全但复杂,将值拆分并重新组合 .
最后一点可以以一种近乎优雅的方式完成,有人在早些时候发布了对此问题的语言律师变体的回复 . 不幸的是我没想到 . 但我推荐简单的
static_cast
,经过适当的测试 .你想使用
static_cast
,类似于:在2的补码中,简单地转换值是有效的,因为它包含模2 ^ n的值,即处理与其他类型相同的位模式 . 例如
(long)0xFFFFFFFFu
返回-1然而,事实是加法和减法都产生了1个进位/借位 . 还需要将一个位与低32位一起存储在一些位置 . 因此,只需将值转换为
signed
并相互之间相差很远即可减去signed
. 尝试使用LONG_MAX - LONG_MIN
或LONG_MIN - LONG_MAX
查看结果如何无法存储在long
中,即使两个操作数均为long
s为了克服这一点,唯一的方法是使用更广泛的类型
或手动处理大int算术
如果在函数中使用它,则必须同时返回溢出进位和低32位差异,因此第一种解决方案在32位或64位计算机上似乎更容易,而第二种解决方案在8位时更快像ATmega这样的MCU
如果你可以保证两个操作数永远不会超过
LONG_MAX
那么一个简单的static_cast
到long将会起作用