【问题标题】:How do I return 0 using bitwise operations?如何使用按位运算返回 0?
【发布时间】:2013-04-16 17:43:07
【问题描述】:

我有以下功能

int sign(int x) {

  int sign = (x >> 31);
  sign = sign | 1;
  return sign;

}

我希望:

  • 为负数返回 -1(第一行)
  • 为正数返回 1(第二行)
  • 零返回 0

我被困在最后一项上。如果 x 为零,如何使用位运算符仅返回零?

【问题讨论】:

  • 为什么要使用按位运算?一个答案是if (x == 0) return 0;,但这不算数吗?
  • 另外,考虑使用sizeof 而不是硬编码尺寸
  • sign = sign | !!x; 算不算?
  • 如果这不是纯粹的智力练习:不要。 if/else 可能看起来并不陌生,但它确实有效而且很容易看到它有效
  • return (x) ? ((x>>31) ? -1 : 1) : 0;

标签: c bit-manipulation


【解决方案1】:

不错的小谜题。假设你有 32 位整数:

int sign( int x )
{
   // if any bit is on, all bits are on. else zero.
   int lowBit = x;
   lowBit |= lowBit >> 16;
   lowBit |= lowBit >> 8;
   lowBit |= lowBit >> 4;
   lowBit |= lowBit >> 2;
   lowBit |= lowBit >> 1;
   lowBit &= 1;

   int signBit = x & 0x80000000;
   signBit |= signBit >> 16;
   signBit |= signBit >> 8;
   signBit |= signBit >> 4;
   signBit |= signBit >> 2;
   signBit |= signBit >> 1;
   signBit &= 0xFFFFFFFE;

   return signBit | lowBit;
}

这是它的工作原理。我将用 4 位数字进行解释,但解释是泛化的。您必须从三类位模式进行映射:

0000    -> 0000
0xxx    -> 0001
1yyy    -> 1111

其中 x 可以是除 000 之外的任何值,而 yyy 可以是任何值。

所以首先你想要一个位,如果寄存器中的任何位为 1,则为 1,否则为 0。所以我们“涂抹”寄存器,这样如果任何位为高,所有位都为高,并且我们保留该位作为结果中低位的值。

然后我们“涂抹”符号位。最后我们将它们“或”在一起。

读者练习:如果你有 32 位整数,但有 64 位寄存器,你可以将操作次数减少一半。

更新:总是可以做得更好(未经测试):

int sign( int x )
{
   int lowBit = !!x;  // double ! operator, wtf? yes...

   int signBit = x & 0x80000000;
   signBit |= signBit >> 16;
   signBit |= signBit >> 8;
   signBit |= signBit >> 4;
   signBit |= signBit >> 2;
   signBit |= signBit >> 1;
   signBit &= 0xFFFFFFFE;

   return signBit | lowBit;
}

【讨论】:

  • 以上代码依赖于实现定义的行为(负数右移)。
  • 想一想——无论哪种方式都可以。理智的平台将用零填充所有右移数字。哑平台将用 1 填充负数,用 0 填充正数。无论哪种方式代码都有效。它只会在用 1 填充正数的疯狂平台上失败。
  • 或最高位不是符号位的平台。例如,它可能是奇偶校验位或用于指示陷阱值。这是 6.2.6.2(2) 允许的。此外,您的代码将负零视为小于零而不是等于零。
  • 我敢让你找到一台不在博物馆里的电脑,它使用其中任何一种。
  • 我敢于让您找到一台不在博物馆中的计算机,它无法使用 < 运算符比较两个整数。 (顺便说一句,您的问题的答案是Unisys ClearPath Dorado,它仍在积极支持中。)
【解决方案2】:

使用这个

int sign(int x) {

  int sign = (x >> 31);
  sign = sign | (~(1<<((x|(x>>5)|(x>>10)|(x>>15)|(x>>20)|(x>>25)|(x>>30))&0x1F))&0x1);
  return sign;

}

这个想法是用x计数左移1

所以如果x==0,那么第一位将被保留1

如果x!=0,那么第一位将更改为0

然后是sign = sign | (~(First bit))

相当于

sign = sign | (~(1<<x) & 0x1);

但是当我们左移1x 时我们必须小心,因为x 可能>= 类型格式的大小(32) 并且x 可能是负数并且对于两者情况下它是未定义的行为。

所以为了避免上述问题,我将我们的 32 位(x)分成 7 个块,每个块包含 5 位,除了最后一个包含 2 位,然后我为所有块创建了一个 |

p>
y=(bloc1|bloc2|bloc3|bloc4|bloc5|bloc6|bloc7) 

所以如果x!= 0 那么y!=0

如果x==0 那么y==0

我们只需要获取y (&amp;0x1F) 的前 5 位。这里我确定y 不是否定的,y&lt;32

然后我可以用y 换档,而不是用x 换档以避免未定义的行为

【讨论】:

    【解决方案3】:

    你也可以使用这个。和我之前的回答有同样的逻辑

    int sign(int x) {
      int y = x;
      y |= y >> 16;
      y |= y >> 8;
      y |= y >> 4;
      y &= 0xF;
    
      int sign = (x >> 31);
      sign = sign | (~(1<<y) & 0x1);
      return sign;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-03-19
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-21
      相关资源
      最近更新 更多