位运算练习

位运算练习

1. test1

给予⼀个整数 x ,如果它乘 2 之后会溢出则返回 0 否则返回 1 。
例如: test1(0x20000000) = 1
例如: test1(0x40000000) = 0

  • 思路

    这道题是判断一个数乘 2 之后是否会溢出,首先判断什么情况下乘2会导致溢出,乘2即是二进制表示下左移1位,当二进制表示中最高位和次高位不同时,左移一位,会导致符号位变化,产生溢出,所以要对x的最高位和次高位进行分析,在这一过程中,要注意高位扩展问题。

    通过将最高位右移一位和次地位做异或运算,而后在右移三十位,将所得结果放在二进制序列中的最后一位,此时最后一位的结果和要求的结果相反。为得到正确结果,需将最后一位结果取反,并令最后一位之外的位为0,对于整数,最后一位之外的已经是0,对于负数,(x>>1)^x之后,符号位变为0,扩展后,除最后一位外也都已经是0,最后,直接和0x1进行异或运算,即可完成。

  • 代码

    int test1(int x)
    {
      return (((x >> 1) ^ x) >> 30) ^ 0x1;
    }
    

2. test2

给予三个数 x 、 y 和 n ,比较 x 和 y 的第 n 字节,如果相同则返回 0 ,不同则返回 1 。
例如: test2(0x12345678, 0x87654321, 1) = 1
例如: test2(0x12345678, 0x87344321, 2) = 0

  • 思路

    这道题是比较两个数的第n个字节是否相同,要注意的是,一个字节有8位,所以要对n*8即左移3位。首先对两个数进行异或操作,而后将其移动到末8位,和0xff进行异或操作,若两数相同,此时为0,通过两次逻辑非运算,结果还是0,答案正确,若两数不相同,进行异或操作后,为十进制1~255之间的数,通过两次逻辑非运算,则返回1,答案正确。

  • 代码

    int test2(int x, int y, int n)
    {
      return !!(((x ^ y) >> (n << 3)) & 0xff);
    }
    

3. test3

给予两个数 x 和 y ,如果 x<=y 则返回 1 ,否则返回 0 。
例如: test3(4,5) = 1

  • 思路

    这道题是判断两个数x,y之间的大小,x<=y返回1,否则返回0。根据eflags思想,判断大小,通过判断OF,CF标志,所以y-x,即y+1+~x,令sub=y-x。此时得到的二进制数的符号位即我们想要的CF标志,若符号位是1,即CF=1。进而判断OF标志,当产生溢出时,x和y一定异号,且sub的符号位一定和y相反,将y和sub,x和y进行异或运算,再将这两个结果进行与运算,即得到了OF标志,溢出时,OF=1。当OF=0,CF=1,和OF=1,CF=0时,x>y。所以将((sub ^ y) & (x ^ y))和sub进行异或运算,而后右移31位,将其移至末位,最后通过逻辑非运算,使得x>y时返回0,x<=0时返回1

  • 代码

    int test3(int x, int y)
    {
      int sub = y + 1 + ~x;
      return !((((sub ^ y) & (x ^ y)) ^ (sub)) >> 31);
    }
    

4. test4

予⼀个整数 x , 如果该整数的⼆进制形式包含奇数个 1 则返回 1 ,否则返回 0 。
例如: test4(5) = 0, test4(7) = 1

  • 思路

    这道题,我一开始先想到不断进行右移,然后和0x1相与,将得数加起来,再通过得到的值的末位来进行判断(此时得到的值即1的个数,末位是1,代表有奇数个1)。

    后来,感觉上述方法用的算符太多,而后想到合并,先每两个相邻位之间进行比较,有00,11,01,10,这四种情况,01,10是奇数个1,00,11是偶数个1,所以只要每两个相邻位进行一次异或运算,即可知两个位里有奇数还是偶数个1,然后将所得结果储存在相邻位中靠近末尾的一位中。x ^ (x >> 1),x右移1位,将相邻位对齐,而后进行异或运算。在之后是在4个位之中考虑,由于以得出两个位中的1的奇偶情况,接下来,将相邻两个存储了奇偶情况的位进行异或运算即可知4个位之中1的奇偶情况。同理,推出每8个位中的1的奇偶情况,每16个位中的1的奇偶情况,最后得出32位中1的奇偶情况。

  • 代码

    int test4(int x)
    {
      x = x ^ (x >> 1);
      x = x ^ (x >> 2);
      x = x ^ (x >> 4);
      x = x ^ (x >> 8);
      x = x ^ (x >> 16);
      return x & 0x1;
    }
    

5. test5

给予⼀个⽆符号整数表示的浮点数 uf (你可以认为 uf 具有浮点数的⽐特级结构) ,函数返回它的相反数,即 -f (返回的结果 也是⼀个⽆符号整数表示的浮点数),如果这个数是 NaN ,请返回它本身。

  • 思路

    这道题,在于判断非数(NaN),当uf的阶码全1,尾码不为全0时,即为NaN,所以将uf和0x7FFFFFFF进行与运算,若其是非数,则阶码必然为全1,即所得结果的前9位是011111111,由于尾码不为全零,所以后23位不为全0,即所得结果需要大于0x7F800000,大于时为NaN,返回这个数本身,否则返回-uf,通过对符号位加1,得到-uf

  • 代码

    unsigned test5(unsigned uf)
    {
      if ((uf & 0x7FFFFFFF) > 0x7F800000)
        return uf;
      else
        return uf + 0x80000000;
    }
    

6. test6

给予⼀个⽆符号整数表示的浮点数 uf (你可以认为 uf 具有浮点数的⽐特级结构) ,函数返回它对应的强制类型转换后的整数(具有整数的⽐特级结构),即实现表达式 (int)uf ,如果这个数超过整数表示的范围(包括 NaN 和⽆穷),请返回 0x80000000u 。

  • 思路

    这道题,首先判断这个数是否超过整数的范围,为方便判断,先将符号位化为0,即和0x7fffffff进行与运算。最大的int型整数即2147483647,用浮点数表示,即为4f000000。大于时,直接返回0x80000000u。

    为实现(int)uf,分别将阶码,尾码,符号位分离,阶码(uf >> 23) & 0xff-127,(127为偏置常数)这里写为150 - ((uf >> 23) & 0xff),是因为result后来需要tail>>(23-exp),为减少运算符,故做此更改。尾码要注意,尾码具有隐含的1,故在之后和 0x00800000做或运算,即加上隐含的1,得到尾码后,通过和阶码相运算,得到对应数值,和符号sign相乘,得到最终结果。

  • 代码

    int test6(unsigned uf)
    {
      int sign = -1, exp = 1, tail = 0, result = 1;
      if ((uf & 0x7FFFFFFF) > 0x4f000000)
      {
        return 0x80000000u;
      }
      else
      {
        exp = 150 - ((uf >> 23) & 0xff);
        if ((uf & 0x80000000) == 0)
          sign = 1;
        if (exp > 23)
          return 0;
        tail = (uf & 0x007fffff) | 0x00800000;
        result = tail >> exp;
        return sign * result;
      }
    }
    
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页