问题 使用少于5个按位运算符实现“逻辑非”


作为我的CS课程的一部分,我最近完成了非常受欢迎的“数据实验室”作业。在这些赋值中,您应该使用尽可能少的操作在C中实现简单的二进制运算。

对于那些不熟悉“数据实验室”的人,可以快速了解规则:

  • 您不能调用函数,强制转换或使用控制结构(例如,如果)
  • 您可以分配没有运营商成本的变量,但只允许使用int)
  • 您使用的操作员越少越好
  • 您可以假设sizeof(int)== 32
  • 负数用2的补码表示

任务是通过仅使用以下运算符来实现一个不称为'bang'的逻辑(其中bang(x)返回!x):〜&^ | + << >>

函数原型定义为

int bang(int x)

我能找到的最佳实现(使用5个运算符)如下:

return ((x | (~x +1)) >> 31) + 1

然而,似乎有一种方法可以通过更少的操作员来实现这一目标,因为我从一些德国大学找到了一个结果网站[1],其中两个人显然找到了一个少于5个操作员的解决方案。但我似乎无法弄清楚他们是如何实现这一目标的。

[1] http://rtsys.informatik.uni-kiel.de/~rt-teach/ss09/v-sysinf2/dlcontest.html (logicalNeg列)

澄清一下:这不是关于如何解决问题,而是如何用较少的操作来解决问题。


11469
2018-05-06 23:03


起源

可能重复 使用C中的按位运算符检查数字是否为非零 - Keith Nicholas
不是真的重复,因为我已经知道如何实现这一点。我正在寻找如何在更少的操作中实现这一目标。 - ntldr
事情是,这个问题实际上已经被多次询问了,真的不需要它的另一个版本 - Keith Nicholas
我无法看到以更简单的方式这样做的问题在哪里被提出或回答。同样,这不是解决这个问题的问题,而是更简单的解决方案(显然存在)的问题。 - ntldr
你得+,但不是 - ?你没有合乎逻辑的右移? - user2357112


答案:


只是轻微作弊:

int bang(int x) {
    return ((x ^ 0xffffffffU) + 1UL) >> 32;
}

是我能想到的只有3个操作的唯一方法。假设32位int和64位长...


5
2018-05-07 03:24





如果您冒昧地假设int加法溢出是明确定义并且包装(而不是未定义的行为),那么有一个包含四个运算符的解决方案:

((a | (a + 0x7fffffff)) >> 31) + 1

我认为你假设溢出被定义为包装否则你的功能 ((x | (~x + 1)) >> 31) + 1 具有x = INT_MIN的未定义行为。


3
2018-05-07 05:39





为什么不只是: -

int bang(int x)
{
    return 1 >> x;
}

2
2018-05-07 00:15



负迁移是UB ...... - Iskar Jarak
链接分配的说明说:“您可以假设您的机器......当移动整数超过字大小时,行为具有不可预测的行为。”尽管如此,获得3-op得分的人可能会使用类似的想法。 - user2357112
@ user2357112:我没有看到“链接分配”,只是指向结果表的链接。你(或某人)可以告诉我吗? - Michael Burr
@ user2357112同样没有看到链接的作业,你看了吗?无论如何,当然转移了 int 大于宽度 int 也是UB(见 6.5.7.3) - Iskar Jarak
@dddsnn右移一个负数CAN在1s内移位以保持数字为负,所以-4 >> 1 == -2 - Keith Nicholas