【问题标题】:How to return 1 if x*x can fit in a 32-bit integer, and 0 otherwise?如果 x*x 可以放入 32 位整数,如何返回 1,否则返回 0?
【发布时间】:2019-03-18 02:39:21
【问题描述】:

我正在研究一个问题,我应该编写一个方法 如果 x*x 可以放入 32 位整数,则返回 1,否则返回 0。
我认为我可以检查数字是否大于 46340(可以使用 2 的补码二进制表示表示的最大数字的平方根。)但是我不能,因为我最多只能使用 255。

 /*
 * squaredOK - Return 1 if x*x can 
fit 
 in 
 a 32-bit integer, and 0 otherwise.
  *  Examples: squaredOK(10) = 1
 *            squaredOK(1000000) = 0
 *  Legal ops: ! ~ & ^ | + - << >>
 *  Max ops: 20
 */
int squaredOK(int x) {

return ; /* no idea */
}

好的,在阅读了 cmets 之后,我决定添加限制: 仅使用以下内容:

  • 整数常量 0 到 255 (0xFF),包括 0 到 255 (0xFF)。不允许使用 0xffffffff 等大常量。
  • 函数参数和局部变量(无全局变量)。
  • 一元整数运算! ~
  • 二进制整数运算 & ^ | + >

【问题讨论】:

  • 提示:INT_MAX 值的平方根是多少?
  • 另外,这个作业是考虑不周,正确的做法是检查-46340 &lt;= x &lt;= 46340。您被允许使用的运算符不能真正做到这一点,因此答案需要一些非常糟糕的特定于实现的位旋转
  • 如果你不能使用更大的常量,那么只需使用小的常量和&lt;&lt;| 来制作更大的常量。
  • 那么&gt;&lt; 是否允许?或者只是&gt;&gt;
  • (a) 可以使用INT_MAX 吗? (b) 可以使用&lt; 这样的比较运算符吗? (c) xx 是否适合理论上的 32 位二进制补码整数或 xx 适合它所运行的 C 实现中的 int 类型吗? (d) 可以使用减法、除法和乘法吗? (“二进制整数运算。”)

标签: c binary bit-manipulation bitwise-operators


【解决方案1】:

这个问题的定义很模糊,我认为你对它的解释使你自己比需要的更难。

如果您将“适合 32 位整数”解释为 无符号 32 位整数,则最大值 (232) 的平方根有更方便的值(216)。我很确定这就是预期的结果。

【讨论】:

  • 我不确定这应该是一个答案。 “问题被误解了”更多的是评论而不是答案。
【解决方案2】:

根据代码注释,&lt;&lt;| 是允许的,因此您可以使用 shift 和 or... 生成更大的常量。将数字转换为十六进制时更简单:

 46340 == 0xb504 == (0xB5 << 8) | 0x04

这并不能完全解决问题,因为您似乎没有可用的比较(这个限制是后来添加的)。

在这种情况下,我希望这个问题是针对无符号整数的。在这里,您只需要检查是否设置了 16 个高位中的任何一个。幸运的是,! 在允许的操作列表中,因此您可以不应用到右移 16 的数字。

如果问题实际上是有符号数,那就更棘手了。您可以使用- 并检查符号位来替换比较运算符:

int squaredOK(int x) {
   return (x - ((0xB5 << 8) | 4)) >> 31 
}

为了更加安全,您可以应用一个双非 (!!) 来防止带有符号数字的右移的实现特定行为。

【讨论】:

  • 我认为在这一步之后这将是微不足道的,直到我意识到比较不可用。我已经相应地扩展了答案。
  • 有2个问题:你需要将46340减一为负测试并且缺少;。此外,您必须将表达式转换为无符号 &gt;&gt; 31 才能具有定义的行为。
【解决方案3】:

您想以一些愚蠢的限制实现abs(x) &lt; 46341(或0xb505)。

这个问题对于签名的 32 位 int 来说似乎相当困难,对于普通的 int 来说就更难了。

如果问题表述错误并适用于 32 位无符号整数,这里有一个解决方案:

 /*
  * squaredOK - Return 1 if x*x can fit in a 32-bit integer, and 0 otherwise.
  *  Examples: squaredOK(10) = 1
  *            squaredOK(1000000) = 0
  *  Legal ops: ! ~ & ^ | + - << >>
  *  Max ops: 20
  */
int squaredOK(uint32_t x) {
    return !(x >> 16);
}

如果问题确实适用于具有二进制补码表示的 32 位签名 int,您可以验证这适用于所有值:

#include <limits.h>
#include <stdio.h>
#include <stdint.h>

int squaredOK(int32_t x) {
    return !(x + 0U >> 31) & x - (0xB5 << 8 | 5U) >> 31 |
           x + 0U >> 31 & !(x + (0xB5 << 8 | 4U) >> 31);
}

int main() {
    long long x;
    for (x = INT32_MIN; x <= INT32_MAX; x++) {
        if (squaredOK(x) != (x * x <= INT32_MAX)) {
            printf("failed on %lld\n", x);
            return 1;
        }
    }
    return 0;
}

解释:

x < 46341 && x > -46341
(x >= 0 && x < 46341) || (x < 0 && x > -46341)
(x >= 0 && x - 46341 < 0) || (x < 0 && x + 46340 >= 0)
(x >= 0 && x - (0xB5<<8|5) < 0) | (x < 0 && x + (0xB5<<8|4) >= 0)
(!(x+0U>>31) & (x - (0xB5<<8|5U)) >> 31) | ((x+0U>>31) & !((x + (0xB5<<8|4U)) >> 31)

通过转换为 unsigned 并右移 31 位来测试符号位。 转换为unsigned 是隐式的,其中一个操作数是无符号的,因此x + 0Ux 转换为无符号类型。其他表达式也使用了类似的技巧。

【讨论】:

  • @EricPostpischil 但是x 不能是“负数”——它是uint32_t
【解决方案4】:

答案假定未签名,为您的具体作业修改

如果在第三个或第四个字节上设置了任何位,那么您无法适应它,所以您的老师想要这样的东西:

          //check the third byte for bits      //check the 4th byte for bits
retbool = (((unsigned char)(x >> 16) & 0xFF) | ((unsigned char)(x >> 24) & 0xFF)));

如果设置了这些字节中的任何位,则您无法将正方形放入 32 位 int 中。如果问题是关于 unsigned int 32 这将是结束,但是由于 0xFFFF * 0xFFFF 大于 max signed int,所以对于符号位,您还必须对第二个字节进行一些检查。

因为你永远不可能真的有一个负平方,所以无符号版本应该是一个替代答案,但我没有写这个作业。

编辑(如果位模式是唯一的,您必须屏蔽...)

#include <stdio.h>

int doesFit(int x);

typedef unsigned char uchar;
typedef unsigned short ushort;

#define MAX_POS_SQUARE 46340
#define MAX_NEG_SQUARE -46340

int main(void) {
    int x=MAX_NEG_SQUARE -2;
    int fits = -1;
    int prevoiusly_fit = 0;
    for(;x<MAX_POS_SQUARE+5;x++){
        fits = doesFit(x);
        if(!fits && prevoiusly_fit){
            printf("%d didnt fit and %d did\n",x,x-1);
        }
        else if(fits && !prevoiusly_fit){
            printf("%d fit and %d did not\n",x,x-1);
        }
        prevoiusly_fit = fits;
    }
    return 0;
}

int doesFit(int x){
    uchar bits_left = 16;
    ushort pos_mask = ((0xb5 << 8) | 0x04);
    ushort neg_mask = ((0xb5 << 8) | 0x05);
    ushort x_16_low = (((-x) & 0xff << 8 ) | (-x) & 0xff); //used in negative case
    ushort x_16_high = (-x)>>16; //used in negative case
    //Handle the negative case
    //printf("0x%04x x: 0x%04x x_16_low: 0x%04x x_16_high:", x, x_16_low, x_16_high);
    if(x>>31){
    //how can you tell if a value x < -46340
        if( (x_16_high & 0xFF) | (x_16_high >>8 & 0xFF)){
            //doesnt fit, maybe dont use compliment use ! if accidental promotion occurs
            printf("bailing out when x=%d\n", x);
            return 0;
        }
        while(bits_left){
            --bits_left;
            if(x_16_low & (1 << bits_left)){
                if(!(neg_mask & (1 << bits_left))){
                    return 0;
                }
            } 
            else if(!(x_16_low & (1 << bits_left))){
                if(neg_mask & (1 << bits_left)){
                    return 1;
                }
            }
            //high bits matched with max value bits, cant tell yet, keep masking.
        }
    }
    else{ //handle the positive case
        //how can you tell if a value x > 46340
        if( (x >> 16 & 0xFF) | (x >>24 & 0x7F)){
            //doesnt fit, return false
            return 0;
        }
        while(bits_left){
            --bits_left;
            if(x & (1 << bits_left)){
                if(!(pos_mask & (1 << bits_left))){
                    return 0;
                }
            } 
            else if(!(x & (1 << bits_left))){
                if(pos_mask & (1 << bits_left)){
                    return 1;
                }
            }
            //high bits matched with max value bits, cant tell yet, keep masking.
        }
    }
    return 1; //Must be the exact size to fit to get to this return
}

输出:

-46341 适合而 -46342 不适合

46341 不适合,46340 适合

我觉得我只是浪费了我生命中的一个小时来做​​这些家伙的家庭作业......

【讨论】:

  • 问题是有符号的(正如提到二进制补码和平方根的 46340 所证明的那样),但这个答案是无符号的。这个答案没有回答问题。
  • @EricPostpischil 这是解决他的作业的总体思路,他可以根据自己的特定目的对其进行调整和测试。我不会为他完成他的任务。
  • 这不是一个通用的想法,因为它仅适用于偶数个值位,而带符号的 32 位整数具有奇数个值位。
  • @EricPostpischil 好的,我用屏蔽更新了它,因为位模式不一致。它传递了相关的值范围。
  • @EricPostpischil 你能解释一下为什么编辑后的评论仍然值得你投反对票吗?
猜你喜欢
  • 1970-01-01
  • 2015-01-18
  • 2015-12-27
  • 1970-01-01
  • 2022-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多