首页 文章

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

提问于
浏览
4

在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 回答

  • 0

    关于通过比较两个未签名计数器来检查已经过了多少时间的更深层/原始问题,其中可能有一个单一的环绕:

    简单地使用无符号算术从最新的中减去最早的 .

    假设你的 currTimeLo 是当前时间的计数器值,并且 time4 是某个较早的值,并且它们都是无符号类型(或者有符号类型的那个提升了另一个的无符号类型),

    return currTimeLo - time4;
    

    这是因为C保证以2n为模执行无符号算术,其中n是无符号类型的值表示中的位数 .

    如果有超过1个回绕,这种方法将无效 . 在这种情况下,您需要使用数字范围较大的类型 .


    关于问题 Headers 将无符号值解释为2的补码有符号值的问题:

    首先请注意,没有必要 . 它是X / Y问题中的Y.获得最新可能包含的两个计数器之间的差异是原始X,它有一个简单的解决方案(上图) .


    但是,因为这是 Headers 中的问题:

    据我所知,所有现存的C实现都是针对有符号整数使用2的补码表示的体系结构 .

    当原始值无法在该类型中表示时,Holy Standard™将其留给实现来定义转换为有符号整数类型的结果 . 任何合理的C实现都只是让你通过 static_cast 来实现 . 因此,

    return static_cast<long>(time4) - static_cast<long>(currTimeLo);
    

    But 无法保证Arduino中的编译器在此方面是合理的 .

    您必须检查,并在必要时使用相关选项,假设默认情况下如果行为不合理则可以调整行为 .

    解决方法包括

    • 通过 reinterpret_cast 转换指针或引用,

    • 通过例如复制字节 memcpy ,正式安全但复杂且不必要的潜在低效,

    • 使用正式的UB联盟成员访问,或

    • 安全但复杂,将值拆分并重新组合 .

    最后一点可以以一种近乎优雅的方式完成,有人在早些时候发布了对此问题的语言律师变体的回复 . 不幸的是我没想到 . 但我推荐简单的 static_cast ,经过适当的测试 .

  • 2

    你想使用 static_cast ,类似于:

    static_cast<signed long>(your_unsigned_long)
    
  • 0

    在2的补码中,简单地转换值是有效的,因为它包含模2 ^ n的值,即处理与其他类型相同的位模式 . 例如 (long)0xFFFFFFFFu 返回-1

    然而,事实是加法和减法都产生了1个进位/借位 . 还需要将一个位与低32位一起存储在一些位置 . 因此,只需将值转换为 signed 并相互之间相差很远即可减去 signed . 尝试使用 LONG_MAX - LONG_MINLONG_MIN - LONG_MAX 查看结果如何无法存储在 long 中,即使两个操作数均为 long s

    为了克服这一点,唯一的方法是使用更广泛的类型

    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位计算机上似乎更容易,而第二种解决方案在8位时更快像ATmega这样的MCU

    如果你可以保证两个操作数永远不会超过 LONG_MAX 那么一个简单的 static_cast 到long将会起作用

    return static_cast<long>(time4) - static_cast<long>(currTimeLo);
    

相关问题