【问题标题】:understanding the behaviour of unsigned numbers理解无符号数的行为
【发布时间】:2016-03-10 09:22:09
【问题描述】:

我的目标是有一个无符号整数值的升序排序列表,我使用sub_func 来选择哪个更大。

sub_func 返回是int,因为我想比较所有数字中哪个更大,但我在定义 unsigned int 的最大值时遇到了问题,即 (0xffffffff == -1) .我想在这里了解更多关于无符号数字的信息。我该如何解决这个问题?

我有以下代码:

#define p_max (0xffffffff)
uint32 a = p_max;
uint32 b = 20;

int sub_func(uint32 a, uint32 b) 
{
    return (b - a);
}

rerun 是 21,我希望它是 20 - MAX,这将是负数,我的返回类型是负数。

但是,我看到它总是一个正数。我不想要,如何解决这个问题?

【问题讨论】:

  • a < bfalse 时,为什么不采用返回true 的标准方式?想想a = b = max 的情况,你需要一个更广泛的签名类型,例如C++11 的 long long 至少为 64 位。
  • 我想要返回值,它对我进一步的编码很重要。
  • unsigned 值不能为负数。
  • 嗯,当这两个值之间的差异大于0x7fffffff时,你的计划是什么?
  • 您确定需要签名差异吗? +/- 或 +1/0/-1 还不够吗?

标签: c++ c unsigned


【解决方案1】:

(假设uint32是一个32位无符号整数类型)。

b - auint32 类型的表达式,因为两个操作数都属于该类型。由于uint32 的标准定义环绕 行为,它的值为21。

您将其分配给 int 类型,因此返回值。

【讨论】:

    【解决方案2】:

    不要混合intuint,除非确实需要,因为最大值不同,因为一位将用于符号。

    根据您的最新评论,您可以返回 0 或 1,然后决定添加到尾/后或头/前。

    uint32 sub_func(uint32 a, uint32 b)  
    {
        if( a > b )
            return 0;
        else
            return 1;
    }
    

    如果要返回 true 或 false,也可以使用 bool 数据类型。根据比较和返回数据类型调整你的函数返回码。

    【讨论】:

      【解决方案3】:

      你问的是不可能的。

      20 - 0xfffffff = -4294967275
      

      不能用 int 表示,限制在[-2147483648,2147483647] 范围内。

      两个无符号的差需要33位!

      【讨论】:

      • 那么,如果我将 p_max 声明为 0x0fffffff,那会起作用吗?
      • @Invictur 这是一个解决方案,是的,将unsigned 限制为signed 的最大值
      • Type int 不限于该范围。
      • @2501:你能详细说明一下吗?
      • @2501,请您详细解释一下这将如何影响?
      【解决方案4】:

      首先,相同类型的signedunsigned 存储在相同大小的相同空间中(例如signed charunsigned char 存储在1 个字节上)。因此,对于signed,一半的可能值用于存储正值,另一部分可能值用于存储负值。对于unsigned,所有可能的值仅用于正值:

      • signed char : -127 到 127
      • unsigned char : 0 到 255

      要计算一个负数,最常用的解决方案是“补码”。存储 -1 :

      • 使用值 1 : 0x01
      • 反转它:0xFE
      • 加1:0xFF

      因此,-1 在有符号上下文中存储为 255 在无符号上下文中。

      这是因为每个人都说:不要混合签名和未签名!

      或者,如果您真的需要,请使用更大的尺寸来计算签名并小心

      std::uint32_t a;
      std::uint32_t b;
      
      std::int64_t = static_cast<std::int64_t>(b) - static_cast<std::int64_t>(a)
      

      8 位和 16 位大小数据的示例:http://coliru.stacked-crooked.com/a/38076c588de3907d

      【讨论】:

      • 你没有把 unsigned 和 signed char 弄错了吗?此外,在 C++14 之前,signed char 可以从 -127 到 +127。
      【解决方案5】:

      您将结果存储在int 中这一事实并不能阻止使用unsigned 算术进行减法。

      两个uint32 的减法总是产生uint32 类型的结果。如果减法的数学结果是否定的,则使用模算术产生一个介于0 和无符号类型可以表示的最大值之间的值。

      你已经利用这个事实从-1 得到0xFFFFFFFF。它同样适用于减法结果。

      20 - 0xfffffff 给出的值为-4294967275。以0x100000000 为模(uint32 可以表示的最大值加上1)是210x15)。 21 转换为 int 仍然是 21

      产生负值的一种方法是避免使用unsigned变量进行减法,例如;

      int sub_func(uint32 a, uint32 b) 
      {
          return (a > b) ? 1 : ((a < b) ? -1 : 0));
      }
      

      【讨论】:

      • 对不起,这太疯狂了 :-)
      • 这是我认为最好的答案:结果来自于计算机不是在做常规算术,而是在做模算术。
      【解决方案6】:

      由于带符号的 32 位 int 的限制是 [-2147483648,2147483647],因此它不能保存减法的结果,因为它超出了限制。您可以使用 64 位数据类型来保存减法的结果。我在 Visual Studio 上测试了以下代码。

      #define p_max 0xffffffff
      
      unsigned int a = p_max;
      unsigned int b = 20;
      
      signed long long sub_func() 
      {
          return ((signed long long)b - (signed long long)a);
      }
      
      int main()
      {
          signed long long x = sub_func();
      }
      

      【讨论】:

      • 返回类型是int。
      【解决方案7】:

      在下面的代码中,问题是为什么 nResult 会是 21

      #define p_max (0xffffffff)
      uint32 a = p_max;
      uint32 b = 20;
      
      void main()
      {
          int nResult = b - a;
      }
      

      您正在尝试计算:

      0000 0000 0000 0000 0000 0000 0001 0100 - 1111 1111 1111 1111 1111 1111 1111 1111

      实际上计算为加法

      11111 1111 1111 1111 1111 1111 1111 1111 的补码)到

      0000 0000 0000 0000 0000 0000 0001 0100.

      = 0000 0000 0000 0000 0000 0000 0001 0101

      结果是21,不考虑符号,因为它是由两个uint32的运算产生的。因此,即使您将结果值转换为有符号整数,它也不会做出任何改变,因为它已经是一个正值。

      【讨论】:

        【解决方案8】:

        我刚刚用 VC2013 测试了这个,编译器将这些值视为有符号整数,因此得到 21!这是不合时宜的:-

        int sub_func(uint32 a, uint32 b)
        {
        00B813C0  push        ebp
        00B813C1  mov         ebp, esp
        00B813C3  sub         esp, 0C0h
        00B813C9  push        ebx
        00B813CA  push        esi
        00B813CB  push        edi
        00B813CC  lea         edi, [ebp - 0C0h]
        00B813D2  mov         ecx, 30h
        00B813D7  mov         eax, 0CCCCCCCCh
        00B813DC  rep stos    dword ptr es : [edi]
        return (b - a);
        00B813DE  mov         eax, dword ptr[b]
        00B813E1  sub         eax, dword ptr[a]
        }
        00B813E4  pop         edi
        00B813E5  pop         esi
        00B813E6  pop         ebx
        00B813E7  mov         esp, ebp
        00B813E9  pop         ebp
        00B813EA  ret
        

        【讨论】:

        • 无符号处理有何不同??
        • 我想它可以支持某种形式的下溢检测,或者可能是类型转换错误(可能缩小转换为 int),我不知道详细的标准,但它确实解释了为什么结果是 21 (20 - -1) 而不是大负值 (20 - p_max)。
        • 一点也不。 C 中没有整数上溢/下溢,只是未定义的行为(您是否看到任何有关进位或溢出标志的测试?)。你能在那个程序集中找出一条有符号或无符号的指令吗??
        • 是的,我知道这一切,我只是想解释在 IA86 处理器上观察到的行为(其他处理器可能会有所不同)。 CPU不是总是做无符号整数加减法,它只是定义符号的结果的解释。编译器可能会对此产生警告,或者您可以在 ASM 块中的 add/sub 之后添加 INTO 指令。..
        • 关键是看那个汇编代码绝对不会告诉你什么。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-21
        • 2011-11-14
        • 1970-01-01
        • 1970-01-01
        • 2015-06-23
        • 2016-02-24
        • 2011-09-11
        相关资源
        最近更新 更多