【问题标题】:Tell if a 32 bit signed int is a power of 2判断 32 位有符号整数是否是 2 的幂
【发布时间】:2011-11-13 19:38:01
【问题描述】:

我需要确定一个有符号的 32 位数字是否是 2 的幂。到目前为止,我知道要做的第一件事是检查它是否为负数,因为负数不能是 2 的幂。 然后我需要看看下一个数字是否有效等等......所以我可以这样写:

// Return 1 if x is a power of 2, and return 0 otherwise.
int func(int x)
{
     return ((x != 0) && ((x & (~x + 1)) == x));
}

但对于我的任务,我只能使用其中的 20 个运算符:

! ~ & ^ | + << >>

并且没有相等语句或循环或转换或语言构造。

所以我正在尝试转换相等部分,我知道 !(a^b) 与 a == b 相同,但我似乎无法完全弄清楚。关于如何将其转换为允许的运营商的任何想法?

【问题讨论】:

  • 看看是否有人可以找到一种方法来回答这个问题, 只是给你答案。
  • 在这种情况下使用的一个非常有用的技巧是!!x。这使数字标准化,因此 0 变为 0 而非 0 变为 1。它甚至更有效(编译器对其进行了优化)。
  • 我总是遇到问题 !!x 所以如果 x = 2 然后 !x = 1 然后 !!x = 0。如果 x = 0, !x = 0 和 !!x = 1 个?
  • 我可以打开一个新的 stackexchange 站点提案,用于“不称职的 CS 讲师 X 的 do-everything-with-!~&amp;^|+&lt;&lt;&gt;&gt; 问题”吗?它已经到了我们有足够的整个网站的地步......
  • 我认为如果讲师也理解 C,这些问题会很好,但是他/她所说的方式,它们在 C 中无法解决,并且所需的“解决方案”充满了未定义的行为(这可以通过切换到无符号算术来解决)。我也认为很不幸,班上的一名学生发现/知道 Stack Overflow,并告诉全班同学来这里为他们完成作业......

标签: c binary logic bit-manipulation


【解决方案1】:

蒂姆的评论让我感到羞耻。让我试着帮助你自己找到答案。

x 在位操作方面是 2 的幂是什么意思?这意味着只有一个位设置为 1。我们怎么能做这样的把戏,将那个位变为 0,而其他一些可能变为 1?那么 & 会给出 0 吗?在单一的表达?如果你发现了 - 你就赢了。

【讨论】:

    【解决方案2】:

    试试这些想法:

    • ~!!x+1 给出一个掩码:如果 x==0 则为 0,如果 x!=0 则为 -1。
    • 如果x 最多设置1 位,(x&amp;(~x+1))^x 给出0,否则为非零,除非~xINT_MIN,在这种情况下结果是未定义的......您也许可以将其拆分为多个部分,并移位到避免这种情况,但我认为你会超过操作限制。
    • 您还需要检查符号位,因为负值不是 2 的幂...

    顺便说一句,听起来你的导师不知道有符号溢出是 C 中的 UB。他应该为 unsigned 整数编写这些问题。即使您想在语义上将值视为已签名,您也需要无符号算术来执行此类有意义的按位运算。

    【讨论】:

    • 你喜欢他们的苹果教练吗?老师通常是没有行业经验的人,这让我一直很痛苦。他们唯一的经历就是玩耍。
    • 我真的不明白。作为一个在学术界的人(当然是数学而不是 CS),我总是期望在学术工作中严格和关注精确的定义。我们一次又一次地看到 CS 作业,其中教师显然不关心他们正在使用的语言的精确定义和规范......
    • 这个问题暗示1 &lt;&lt; 31 应该是假的,因为它是一个负数,而不是 2 的幂。
    • @Peter,对于带符号的 32 位数字 1 &lt;&lt; 31 不属于未定义行为。
    • @Peter:如果你问的是整数溢出,it happens in real life,这种情况并不少见。
    【解决方案3】:

    首先,在您的解决方案中,它应该是

    return ((x > 0) && ((x & (~x + 1)) == x));
    

    因为负数不能是 2 的幂。 根据您的要求,我们需要将“>”、“&&”、“==”转换为允许的运算符。

    首先想到“>”,一个整数>0,当它的符号位为1且不为0时;所以我们考虑

    ~(x >> 31) & (x & ~0)
    

    如果 x 为非正数,上述表达式将返回一个非零数。请注意,~0 = -1,即 0x11111111。我们使用 x & ~0 来检查这个整数是否在每个数字上都是 0。

    其次,我们考虑“&&”。 AND 非常简单——我们只需要获取 0x01 和 0x01 即可返回 1。因此,如果它返回非零数字,我们需要在第一个答案前添加 (!!) 以将其更改为 0x01。

    最后,我们考虑“==”。要测试 A 和 B 的公平性,我们只需要这样做

    !(A ^ B)
    

    所以我们终于有了

    return (!!(~(x >> 31) & (x & ~0))) & (!((x&(~x+1)) ^ x))
    

    这似乎是一个家庭作业问题。请不要简单地复制和粘贴。我的回答有点尴尬,可能会改进。

    【讨论】:

      【解决方案4】:

      想一想……任何 2 减 1 的幂都是一串 0,后跟一串 1。您可以通过x + ~0 实现减一。想一想 1 的字符串从哪里开始与单个 1 的关系,这将是 2 的幂。

      【讨论】:

        【解决方案5】:
        int ispower2(int x)
        {
        
            int ispositive= ! ( (x>>31) ^ 0) & !!(x^0);         
            int temp= !((x & ~x+1) ^ x);
            return temp & ispositive;
        
        }
        

        【讨论】:

        • 欢迎来到 SO 并感谢您提供答案。请考虑为您的答案添加更多上下文以改进它。
        • 请在您的回答中添加一些解释。
        【解决方案6】:

        在 C 中使用位运算符来解决一些问题既有趣又高效。在这个问题中,我们需要处理两个检查:

        1. 符号检查。如果为负,则返回 0;否则返回 1;

          ! (x >> 31 & ox1) & !(!x)

          /* 这个操作。提取 x 中的符号位。但是,在这种情况下,>> 将是算术的。这意味着在最后一位(LSB)之前将全为 1。对于负整数,它是 oxFFFFFFFF(-);否则,oxFFFFFFFE(+)。 AND ox1 运算。将 >> 更正为 ox1(-) 或 ox0(+)。逻辑!将 ox1(-) 和 ox0 (+) 分别变为 0 或 1。 !(!x) 确保 0 不是 power(2)*/

        2. isPower(2) 检查。如果是,则返回 1;否则为 0。

          !( x & (~x + ox1) ^ x )

          /* 这个操作。 isPower(2) 检查。 x & (~x + ox1) 返回 x,当且仅当 x 是幂 (2)。例如:x = ox2 和 ~x + ox1 = oxFFFFFFFE。 x & (~x + ox1) = ox2;如果 x = ox5 和 ~x + ox1 = oxFFFFFFFB。 x & (~x + ox1) = ox1。因此,ox2 ^ ox2 = 0;但 ox5 ^ ox1 = ox4。这 ! op把0和其他分别变成1和0。*/

        最后一个 AND 运算。 1 到 2 次检查将生成 isPower(2) 函数的结果。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-05
          • 2017-02-03
          • 1970-01-01
          • 2021-05-05
          相关资源
          最近更新 更多