【问题标题】:Swap nibbles in a byte以字节交换半字节
【发布时间】:2017-11-04 19:33:30
【问题描述】:

考虑下面的代码,它在一个字节中交换半字节:

#include <stdio.h>

unsigned char swapNibbles(unsigned char x)
{
    return ( (x & 0x0F)<<4 | (x & 0xF0)>>4 );
}

int main()
{
    unsigned char x = 100;
    printf("%u", swapNibbles(x));
    return 0;
}

为什么需要用 0x0F 和 0xF0 进行与运算?相反,我可以写

return ((x<<4) | (x>>4));

我已经用谷歌搜索过它,人们似乎说它不适用于负数,因为它会用右移的数字填充数字但是与 0xF0 并不会使数字为正数? 我错过了什么?

【问题讨论】:

  • 您什么都不会错过,((x&lt;&lt;4) | (x&gt;&gt;4)) 会起作用(只要xunsigned char)。
  • @HolyBlackCat: .... 并且 CHAR_BIT == 8。这是非常罕见的事情,但对于某些 DSP 微控制器可能并非如此,因此符合标准的代码需要
  • AFAIK 这就是 Java 的情况,因为 Java 字节总是有符号并符号扩展为整数。也许代码是由使用 Java 的人移植或编写的。
  • ((x&lt;&lt;4) | (x&gt;&gt;4)) 实际上不会给出正确的结果,因为 x 在执行移位之前被提升为 int。只有在return 中转换为无符号字符,我们才能得到正确的结果。
  • @FalkHüffner 它会起作用,因为unsigned char 总是零扩展为 int,而不是有符号扩展,因此高字节中没有 1

标签: c++ bit-manipulation


【解决方案1】:

这两种变体是 100% 等效的。 gcc 和 clang 都能够将每个代码 sn-p 转换为简单的rol 指令,并将它们编译为相同的汇编代码(当然启用了优化)

unsigned char swapNibbles(unsigned char x)
{
    return (x & 0x0F)<<4 | (x & 0xF0)>>4;
}

unsigned char swapNibbles2(unsigned char x)
{
    return  x << 4 | x >>4 ;
}
swapNibbles(unsigned char):                       # @swapNibbles(unsigned char)
        mov     eax, edi
        rol     al, 4
        ret
swapNibbles2(unsigned char):                      # @swapNibbles2(unsigned char)
        mov     eax, edi
        rol     al, 4
        ret

【讨论】:

    【解决方案2】:

    你的例子没有anding就很好。

    如果swapNibblessigned char 参数,那么人们可能会认为anding 是用来修复表达式的。不过,接下来我们来分析一下表达式:

    • (x&amp;0x0f)&lt;&lt;4:如果x为负数,那么x&amp;0x0f表达式变为非负值,结果符合int/unsigned intx提升的类型),所以可以执行转变
    • (x&amp;0xf0)&gt;&gt;4:如果 x 是负数,那么 x&amp;0xf0 也是负数,所以移位是实现定义的。例如,在 x86 上,这将复制符号位 -> 不是我们想要的。在这种情况下,更好的表达式是 (x&gt;&gt;4)&amp;0xf(仍然定义了实现,但适用于 x86)

    所以这个表达式对signed char 具有实现定义的行为,这种行为不是我们在 x86 上想要的。

    最好将signed char 转换为unsigned char,并对无符号值进行移位。

    注意:我假设 signed char 存储在两个补码中,而 char 是 8 位的(问题中暗示了这一点,因为我们在这里讨论的是 4 位半字节)。

    【讨论】:

    • 移位的结果是否适合unsigned char并不重要,因为x会在移位之前提升为int
    • 是的,您对促销是正确的,我将修改我的答案以反映这一点。然而,这很重要。如果它适合unsigned char,那么它适合int/unsigned int
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-20
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多