【问题标题】:Check if a number is non zero using bitwise operators in C使用 C 中的按位运算符检查数字是否非零
【发布时间】:2011-04-24 03:36:19
【问题描述】:

使用除! 之外的合法运算符检查数字x 是否非零。

示例:isNonZero(3) = 1isNonZero(0) = 0

法律操作:~&^|+<<>>

  • 注意:只能使用位运算符。不能使用ifelsefor等。
  • Edit1:操作员的数量不应超过 10 个。
  • Edit2:考虑int 的大小为 4 个字节。

int isNonZero(int x) {
return ???;
}

使用! 这将是微不足道的,但不使用! 怎么办?

【问题讨论】:

  • 在 C 中,非零数是非零的。您没有明确要求该函数返回 1 或 0(但这是隐含的)。请明确定义您的函数将返回什么。你给出的只是 2 个例子。
  • 至少让函数返回一个布尔值以避免像return x; 这样的答案(是的,我做到了)。一点上下文也会很有趣,为什么你(任何人)需要编写这样一个具有这样约束的函数?
  • + 从什么时候开始是位运算符?
  • 人们真的会在面试中问这种蹩脚的问题吗?它的总 BS(请原谅使用过度技术术语)
  • 这个面试问题的正确答案是:你打算对结果做什么?比较?那么为什么我不能首先进行比较呢?我的时间有更好的事情要做,你没能被选为我的老板。

标签: c bit-manipulation


【解决方案1】:

adamk 函数的对数版本:

int isNotZero(unsigned int n){
  n |= n >> 16;
  n |= n >> 8;
  n |= n >> 4;
  n |= n >> 2;
  n |= n >> 1;
  return n & 1;
};

也是最快的一个,但在组装中:

xor eax, eax
sub eax, n  // carry would be set if the number was not 0
xor eax, eax
adc eax, 0  // eax was 0, and if we had carry, it will became 1

可以用C编写类似于汇编版本的东西,你只需要使用符号位和一些差异。

编辑:这是我能想到的最快的 C 版本:

1) 对于负数:如果设置了符号位,则数字不为0。

2) 为正:0 - n 为负,可以像案例 1 一样检查。我在合法操作列表中没有看到 -,所以我们将使用 ~n + 1 代替.

我们得到了什么:

int isNotZero(unsigned int n){ // unsigned is safer for bit operations
   return ((n | (~n + 1)) >> 31) & 1;
}

【讨论】:

  • 您在这里使用了 11 个位运算符。 (5x|, 5x>>, 1x&)。
  • "sub" 不是按位运算;如果我们允许-1,C++ 解决方案会更容易
  • 你为什么不直接使用 neg? xor eax, eax;否定 ecx; adc eax, 0;
  • isNotZero 解释...如果 n = 0,n 和 -n 都只能是非负数。 (n | (~n + 1)) 本质上是与 -n 进行“或”运算。然后将最高有效位(即符号位)向右移动 31 以将其置于最低有效位位置。然后用 1 "and" 去掉任何符号扩展的符号。
  • 有一个更短的汇编程序解决方案,它不会修改输入变量(通常更好):xor eax, eax; cmp eax, n; adc eax, 0;
【解决方案2】:
int isNonZero(unsigned x) {
    return ~( ~x & ( x + ~0 ) ) >> 31;
}

假设 int 是 32 位(/* 编辑:这部分不再适用,因为我将参数类型更改为无符号 */ 并且有符号移位的行为与无符号移位完全相同)。

【讨论】:

  • 我猜这是假设第二个补码表示 (x + ~0 == x-1)
  • 在我的 Intel 32 位 Linux 机器上,此函数返回 0-1。如果你从零中减去答案,你就会有一个工作函数(在英特尔 32 位和 gcc 上)。
  • @PP:这就是为什么我写了“假设......有符号的班次表现得与无符号班次完全一样”。这表明在带有 gcc 的 Intel 32 位上,它们的行为不同,这完全没问题。事实上,当x 是有符号类型时,这个解决方案会出现更多问题。稍后我会详细说明...
  • 我将参数类型更改为无符号,不仅因为当参数具有负值时,对有符号类型的按位运算的结果是实现定义的,而且还因为不能可靠地使用加减非零常量要么,那么至少有一个 x 值,+- 将导致溢出,从而导致未定义的行为。
【解决方案3】:

为什么要把事情复杂化?

int isNonZero(int x) {
    return x;
}

之所以有效,是因为 C 的约定是每个非零值都表示为真,因为 isNonZero 返回一个合法的 int。

有人认为,isNonZero() 函数应该为输入 3 返回 1,如示例所示。

如果您使用 C++,它仍然像以前一样简单:

int isNonZero(int x) {
    return (bool)x;
}

如果你提供 3,现在函数返回 1。

好的,它不适用于缺少正确布尔类型的 C。

现在,如果您假设整数是 32 位并且允许使用 +:

int isNonZero(int x) {
    return ((x|(x+0x7FFFFFFF))>>31)&1;
}

在某些架构上,您甚至可以避免最终的&1,只需将 x 强制转换为无符号(运行时成本为空),但这是未定义的行为,因此取决于实现(取决于目标架构是否使用有符号或逻辑右移)。

int isNonZero(int x) {
    return ((unsigned)(x|(x+0x7FFFFFFF)))>>31;
}

【讨论】:

  • 因为这不是要求。要求是非零返回 1
  • @Matt Ellen:让 OP 写出来好吗?当他写下他的问题时,他说Example: isNonZero(3) = 1,并且示例不是必需的,从来没有。
  • 好的,但是你的函数也不符合示例。
【解决方案4】:
int is_32bit_zero( int x ) {
    return 1 ^ (unsigned) ( x + ~0 & ~x ) >> 31;
}
  1. 减 1。(~0 在二进制补码机上生成负一。这是一个假设。)
  2. 仅选择翻转为 1 的翻转位。
  3. 如果x 为零,则只有在减一时才会翻转最高有效位。
  4. 将最高有效位移至最低有效位。

我数了六个运算符。我可以使用0xFFFFFFFF 五个。对unsigned 的强制转换不依赖于二进制补码机器;v)。

http://ideone.com/Omobw

【讨论】:

  • 当 x = 0x80000000 时返回 0
  • 也不允许使用 + 和 -,如果你允许它们会更简单。
  • @kriss: + 被明确允许,见列表。
  • @Matt:不,这就是为什么会有unsigned 的演员表。查看 ideone 链接。
  • @usta:是的,没想到。幸运的是,修复很简单 - 使用 AND NOT 代替 XOR。
【解决方案5】:

按位或数字中的所有位:

int isByteNonZero(int x) {
    return ((x >> 7) & 1) |
           ((x >> 6) & 1) |
           ((x >> 5) & 1) |
           ((x >> 4) & 1) |
           ((x >> 3) & 1) |
           ((x >> 2) & 1) |
           ((x >> 1) & 1) |
           ((x >> 0) & 1);
}

int isNonZero(int x) {
  return isByteNonZero( x >> 24 & 0xff ) |
         isByteNonZero( x >> 16 & 0xff ) |
         isByteNonZero( x >> 8  & 0xff ) |
         isByteNonZero( x       & 0xff );
}

【讨论】:

  • 我不确定创建第二个函数是否算作按位运算符 :)
  • 我不是 C 人,我想知道为什么 return x|(x&0); 不起作用?把它放在你的答案中,我会投赞成票。
  • @Spencer Ruport:不,这行不通。这是一个微不足道的身份;它只会返回 x。结果必须是 1 或 0。
  • @JoshD:为什么结果应该是 1 或 0,在 C 中每个非零值都表示为真。身份完美运行 :-) 如果您不相信,请尝试。
  • @kriss:是的,你是对的,但问题要求返回一个值为 0 或 1 的整数,正如你从问题中的函数中看到的那样。否则你只会返回 x ,这将是一个微不足道的问题。
【解决方案6】:

基本上你需要或位。例如,如果你知道你的数字是 8 位宽:

int isNonZero(uint8_t x)
{
    int res = 0;
    res |= (x >> 0) & 1;
    res |= (x >> 1) & 1;
    res |= (x >> 2) & 1;
    res |= (x >> 3) & 1;
    res |= (x >> 4) & 1;
    res |= (x >> 5) & 1;
    res |= (x >> 6) & 1;
    res |= (x >> 7) & 1;

    return res;
}

【讨论】:

  • 我想你有<<,你的意思是有>>
  • 您在这里使用了 24 个位运算符。
  • @haylem:当我写答案时,这不是要求之一
【解决方案7】:

我的解决方案如下,

int isNonZero(int n)
{
    return ~(n == 0) + 2;
}

【讨论】:

  • == 不在合法运营商列表中。
【解决方案8】:

我在 C 中的解决方案。没有比较运算符。不适用于 0x80000000。

#include <stdio.h>

int is_non_zero(int n) {
    n &= 0x7FFFFFFF;
    n *= 1;
    return n;
}

int main(void) {
    printf("%d\n", is_non_zero(0));
    printf("%d\n", is_non_zero(1));
    printf("%d\n", is_non_zero(-1));
    return 0;
}

【讨论】:

  • is_non_zero(-1) 返回 2147483647 = 0x7FFFFFFF(在具有 32 位 2 的补码 int 的系统上,例如 x86:godbolt.org/g/DJFTNu)。 n *= 1; 是一个空操作(请注意,它甚至不会出现在 asm 输出中,is_non_zero 只返回低 31 位)。也就是说,这根本不会产生 0 / 1 返回值。
【解决方案9】:

我的解决方案,虽然与您的问题不太相关

int isSign(int x)

{
//return 1 if positive,0 if zero,-1 if negative
return (x > 0) - ((x & 0x80000000)==0x80000000)
}

【讨论】:

  • 根据问题不允许比较运算符;只有位运算符。
【解决方案10】:
if(x)
     printf("non zero")
else
     printf("zero")

【讨论】:

    【解决方案11】:

    以下函数示例应该适合您。

    bool isNonZero(int x)
    {
        return (x | 0);
    }
    

    【讨论】:

    • 只要确保 bool 被识别为一种类型。在使用 gcc 的 Linux 上,我必须将这些添加到我的 .c 文件中:只需确保将 bool 定义为一种类型。否则我在 Linux 上使用 gcc 执行此操作,因此 bool 将被识别为一种类型。 #define true 1 #define false 0 typedef char bool;
    • "| 0" 完全没用。这里的所有工作都是通过转换为 bool 来完成的,如果您使用的是具有真正 bool 类型而不是 typedef 到整数类型的语言。
    【解决方案12】:

    如果非零,该函数将返回x,否则返回0

    int isNonZero(int x)
    {
        return (x);
    }
    

    【讨论】:

      【解决方案13】:

      int isNonZero(int x)

      {

      if (  x & 0xffffffff)
          return 1;
      else
          return 0;
      

      }

      假设 Int 是 4 个字节。

      如果值不为零,它将返回1

      如果值为零,则返回 0。

      【讨论】:

      • if 语句不允许出现在问题中。
      【解决方案14】:

      返回 ((val & 0xFFFFFFFF) == 0 ? 0:1);

      【讨论】:

      • 这仍然是if,只是简写形式!此外,val &amp; 0xFFFFFFFF == val.
      猜你喜欢
      • 2023-03-18
      • 2023-01-26
      • 1970-01-01
      • 1970-01-01
      • 2016-01-05
      • 2012-06-17
      • 2019-03-07
      • 2011-01-31
      • 1970-01-01
      相关资源
      最近更新 更多