按位运算和转换

时间:2013-02-09 22:49:09

标签: c bit-manipulation bitwise-operators twos-complement bit-shift

我很难理解这段代码的工作方式和原因。我在这个任务中的合作伙伴完成了这一部分,我无法得到他,以了解它的工作原理和原因。我已经尝试了一些不同的东西来理解它,但任何帮助将非常感激。此代码使用2的补码和32位表示。

/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
    int r, c;
    c = 33 + ~n;
    r = !(((x << c)>>c)^x);
    return r;
}

3 个答案:

答案 0 :(得分:15)

c = 33 + ~n;

计算使用n低位后的剩余高位数。

((x << c)>>c

这将使用与x的符号位相同的值填充高位。

!(blah ^ x)

这相当于

blah == x

答案 1 :(得分:12)

  • 在2&#39s补充平台上-n相当于~n + 1。因此,此平台上的c = 33 + ~n实际上等同于c = 32 - n。此c旨在表示如果int低位被占用,则32位n值中有多少高位保留。

    请注意此代码中存在两个平台依赖项:2&#39; s-complement平台,32位int类型。

  • 然后((x << c) >> c旨在签署那些c更高阶位。符号填充意味着x在位位置0中具有n - 1的值,这些高阶位必须被清零。但对于x在位位置1n - 1的值,这些高位位必须用1填充。这对于使代码适用于x的负值非常重要。

    这引入了另外两个平台依赖:<<运算符,当移位负值时或在1移位到符号位(正式地,它是未定义的行为)时>>表现良好; x在移动负值时执行符号扩展的运算符(正式地,它是实现定义的)

  • 如上所述,其余部分仅与!(a ^ b)的原始值进行比较:a == b相当于x。如果上述转换没有破坏x的原始值,那么n确实适合2 {s}补码表示的{{1}}低位。

答案 2 :(得分:3)

Using the bitwise complement (unary ~) operator on a signed integer has implementation-defined and undefined aspects。换句话说,即使您只考虑两个补码实现,此代码也不可移植。

重要的是要注意,即使C中的两个补码表示也可能有陷阱表示。 6.2.6.2p2 even states this非常清楚:

  

如果符号位为1,则应以下列方式之一修改该值:

     

- 符号位0的相应值被否定(符号和幅度);

     

- 符号位的值为 - (2 M)(二进制补码);

     

- 符号位的值为 - (2 M - 1)(补码)。

     

其中哪一个适用于实现定义,是符号位1和所有值位零(前两个)的值,还是符号位和所有值位1(对于“补充”,是陷阱表示或正常值。

重点是我的。 Using trap representations is undefined behaviour

在默认模式下,有一些实际的实现将该值保留为陷阱表示。我倾向于引用的值得注意的是Unisys Clearpath Dordado on OS2200 (go to 2-29)。请注意该文件的日期;这样的实现不一定是古老的(因此我引用这个实现)。

根据6.2.6.2p4,左移的负值也是未定义的行为。我还没有对现实中的行为做过大量的研究,但我会合理地期望可能存在签名扩展的实现,以及没有实现的实现。这也是形成上述陷阱表示的一种方式,其本质上是未定义的并且因此是不期望的。理论上(或者可能是遥远或不远的未来),您可能还会遇到“对应于计算异常”的信号(这是与SIGSEGV属于的类似的C标准类别,对应于诸如“除以零”之类的事情或其他不稳定和/或不良行为......

总之,问题中代码的唯一原因是巧合的是,您的实施所做的决定恰好以正确的方式对齐。如果您使用我列出的实现,您可能会发现此代码对某些值的预期效果不正常。

这样的沉重的魔法(正如评论中所描述的那样)并不是真的有必要,而且对我来说并不是那么理想。如果你想要一些不依赖于 magic (例如可移植的东西)来解决这个问题,可以考虑使用它(实际上,这段代码至少可以用1 <= n <= 64):

#include <stdint.h>

int fits_bits(intmax_t x, unsigned int n) {
    uintmax_t min = 1ULL << (n - 1),
              max = min - 1;
    return (x < 0) * min + x <= max;
}