解释用C ++签名的无符号(长整数)int

时间:2016-01-30 23:47:35

标签: c++ arduino int unsigned signed

我想将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'

3 个答案:

答案 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_MINLONG_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);