【问题标题】:Strange bit twiddling bug奇怪的小虫子
【发布时间】:2011-03-27 00:11:36
【问题描述】:

我有一个简单的函数:

int bitcount( unsigned );

int shiftbit( unsigned val ) {
    return (int)(~0U << (( sizeof(int)*CHAR_BIT ) - bitcount(val)));
}

我正在计算 int 中的位数,然后创建一个位掩码,该位掩码使该位数左对齐。例如,0xABCD 设置了 10 位,返回掩码 0xFFC0000。

该函数工作得很好,除非位计数为零,在这种情况下,我得到 -1,0xffffffff,而我应该得到一个空位掩码,即 0x0。我只是不清楚为什么它应该适用于除零以外的所有情况。

回答

所以我最终将代码更改如下,它工作正常并且应该是可移植的:

int shiftbit( unsigned val ) {
    int bCount = bitcount(val);
    return bCount ? (~0 << (( sizeof(int)*CHAR_BIT ) - bCount)) : 0;
}

【问题讨论】:

    标签: c


    【解决方案1】:

    如果移位的位数大于或等于移位值中的位数,则移位操作的结果是不确定的。因此,将 32 位值移动 32 并不能保证特别产生任何结果。

    【讨论】:

    • @Robert S. Barnes:要么这样,要么在 更宽的 整数类型中执行移位,然后将其转换回。移位~0UL~0ULL,取决于在您的平台上哪个类型比int 更宽,然后在移位后将其转换回unsigned int。此外,按位运算更适合无符号类型。您对签名int 的使用看起来不太好,除非您有充分的理由使用签名类型。
    • @AndreyT:这可能不如明确检查零位计数那么便携。
    【解决方案2】:

    因为在 C 中,如果将大小为 x 位的操作数移动x 位,结果是不确定的。所以例如1 &lt;&lt; 32是未定义的(假设sizeof(int) == 4

    【讨论】:

      【解决方案3】:

      在 C 中,转换的结果取决于实现。使用 x86 处理器,您实际上可以确定移位操作数是模 32(在 32 位模式下)。请参阅本文档的第 3-623 页:

      ftp://download.intel.com/design/intarch/manuals/24319101.pdf

      "目标操作数可以是寄存器或内存位置。计数操作数可以是 立即数或寄存器 CL。 计数被屏蔽为五位,这限制了计数范围 到 0 到 31。为计数 1" 提供特殊的操作码编码

      【讨论】:

      • 这是处理器的移位操作,在x86上这些情况下C的移位操作仍然是未定义的。例如,尝试在 Visual Studio 2010 的发布版本中执行此操作。Visual Studio 将优化“int v = 32; int a = -1
      • x86 系列的原始 16 位版本没有屏蔽移位计数。这意味着我们有二进制兼容的处理器,相同的机器代码具有不同的结果。想知道为什么 C 没有定义这个吗? :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-09
      • 1970-01-01
      • 1970-01-01
      • 2013-05-17
      相关资源
      最近更新 更多