【问题标题】:Bitwise shift operators on signed types有符号类型的按位移位运算符
【发布时间】:2012-11-12 17:30:55
【问题描述】:

我试图了解按位运算符对有符号和无符号类型的行为。根据 ISO/IEC 文件,以下是我的理解。

左移运算符

  • E1 << E2的结果,是E1左移E2位

  • 左移帐户中空出的位将被零填充。

  • E1 为有符号非负数: E1 << E2 将得到 E1 乘以 E2 的 2 次方,如果该值可以由结果类型表示。

    李>
  • Q1:签名的底片怎么样?

  • Q2:我无法理解以下上下文中“约简模”的含义。 "如果 E1 具有无符号类型,则结果的值为 E1 × 2E2 ,减少模 比结果类型中可表示的最大值大一”

右移运算符

  • E1 >> E2 的结果是 E1 右移 E2 位位置。

  • E1 as signed non-negative/unsigned:结果的值是E1 / 2E2的商的整数部分

  • Q3:对于有符号负整数,我看到有些书定义空位将用1填充。请详细说明右移运算符在有符号负整数上的使用。强>

【问题讨论】:

    标签: c bit-manipulation bitwise-operators iso bit-shift


    【解决方案1】:

    Q1:左移运算符对有符号整数类型的负值的行为未定义,当结果E1 * 2^E2 在类型中不可表示时,有符号整数类型的正值的行为也是如此。

    这在标准(n1570 草案)第 6.5.7 节第 4 和第 5 段中明确提到:

    4 E1 << E2 的结果是E1 左移E2 位位置;空出的位用零填充。如果E1 具有无符号类型,则结果的值为E1 × 2^E2,以比结果类型中可表示的最大值多1 为模减少。如果E1 具有带符号类型和非负值,并且E1 × 2^E2 在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。

    Q2:比无符号整数类型中可表示的最大值多一模的归约意味着在左侧移出的位被简单地丢弃。

    从数学上讲,如果无符号类型的最大值是2^n - 1(并且始终是这种形式),则将E1左移E2位的结果是值V,范围为0 到 2^n - 1 这样的区别

    (E1 * 2^E2 - V)
    

    能被2^n整除,也就是说,它是E1 * 2^E2除以2^n得到的余数。

    Q3:右移有符号整数类型的负值时的行为是实现定义的。最常见的行为(至少在二进制补码机器上)是算术移位,也就是说,结果是商向下舍入(朝向负无穷大)。

    5 E1 >> E2 的结果是 E1 右移 E2 位位置。如果E1 具有无符号类型或E1 具有带符号类型和非负值,则结果的值是E1 / 2^E2 商的整数部分。 如果E1 具有带符号类型和负值,则结果值是实现定义的。

    【讨论】:

    • 在 C89 下,为所有平台精确定义了左移负值的行为,其中无符号类型的数据位正好比有符号类型多一位,并且无符号类型中的最高有效位在与有符号类型的符号位相同的位置;其他平台上的行为有效地由实施定义。 C99 将其更改为未定义行为,但基本原理并未提及更改,更不用说提供任何理由。因此,是否定义它的问题取决于是否将 C89 兼容平台用于普通目标。
    【解决方案2】:
    • 回复:第一季度
      如果 E1 为负,则行为未定义。

    • 回复:第二季度
      无符号算术是“循环的”,也就是说,它会环绕,所以UINT_MAX + 1 又是0。就好像每次计算都是以 UINT_MAX+1 为模完成的。另一种思考方式是简单地丢弃不适合左侧的多余位。

    • 回复:第三季度
      如果 E1 为负,则结果是实现定义的。也就是说,它取决于您的机器/编译器/选项,但必须在某处记录(“定义”)行为,通常是编译器手册。两种流行的选择是用 1(算术移位)或 0(逻辑移位)填充左侧的传入位。

    【讨论】:

      【解决方案3】:

      如果您真的想了解按位移位运算符。看看这些简单的规则:

      1) 左移时,E1

      2) 在左移时,E1 >> E2,左侧的所有空位,如果数字为正数,则为 0,如果数字为负数,则为 1。请记住,无符号数永远不会是负数。即使数字是负数,某些实现也可能在某些机器上用 0 填充它们,所以永远不要依赖它。

      这两个简单的规则可以解释所有其他情况。 现在如果你想知道移位后结果的值,只需在纸上手动写数字和移位的位表示,并使用这两个规则在空白处输入位。这样你就可以更好地理解位移是如何工作的。

      For example lets take int i = 7;
      i<<2
      now i = 0000 0000 0000 0000 0000 0000 0000 0111
      perform two left shits according to rule 1 would be:
      
      0000 0000 0000 0000 0000 0000 0001 1100
      

      这将给它的值 28 ( E1 * 2E2 = 7 *2*2 = 28),这与位模式表示的相同。

      现在让我解释一下“减少模”的事情,如果你移动一个大数字,那么左边的位将会丢失,“减少模”会补偿它,所以如果你的结果值大于数据类型可以保持的最大值,左边的位将丢失,然后: 结果 = (E1*2*E2) % ( 最大值 + 1)。

      对于其他各种情况,只要记住以上规则,你就很好了:)

      【讨论】:

      • 您又忘记了一条规则:因为某些处理器具有“移位 N”指令,该指令仅适用于小于字长的 N 值,如果数字要移位的位数超过了左侧操作数的大小。
      【解决方案4】:

      Q2:“value reduction modulo X”在数学中的意思是“value mod X”,在 C 中可以写成“value % X”。这部分只是解释了整数溢出的工作原理。

      【讨论】:

        猜你喜欢
        • 2015-03-24
        • 2022-12-06
        • 1970-01-01
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-25
        相关资源
        最近更新 更多