C语言之位运算原码反码补码、进制间的转换、常规应用

    xiaoxiao2021-03-28  36

    所谓位运算,就是对一个比特(Bit)位进行操作。比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。

    ****进制间的转换:

     16进制转换成10进制:例如0xff:  15*16^1+15*16^0=255。

    10进制转换成16进制:用十进制数整除以16,然后取余数,直到商为0则停止转换。余数可以是0~15中的某一个数,其中0~9不用改变,10~15则分别用A~F表示。最先得到的余数是最低位,最后得到的是最高位,由高到低排列得到16进制数(逆序)。(也可以先八10进制转换成二进制,再把二进制转换成16进制.)

     

    二进制转成10进制:数字中所有位*本位的权重然后求和,注意如果二进制有小数部分,小数部分的权重指数是负的;例如10101.101=1*2^4+0*2^3+1*2^2+0*2^1+1*2^0+1*2^-1+0*2^-2+1*2-3=21.625,

    10进制转换二进制:将10进制数字不断除以2直到商为零,然后将余数由下至上依次写出(逆序),即可得到该数字的二进制表示.

    10进制小数转换成二进制:十进制小数转R进制小数(其他进制方法类似),方法为乘R取整,每次乘以相应之后基数后取结果的整数部分即可,直到小数部分为0,但是并非所有的十进制小数都能完全转化为R进制小数,这时就需要根据要求的精度值,类似四舍五入(比如二进制0舍去,1进位)。例如:

    0.625*2=1.25  整数1

    0.25*2=0.5      整数0

    0.5*2=1.0         整数1

    结果按顺序排列得到0.101;

     

    二进制转换成八进制:从右向左,每三位一组(不足三位的在左侧补0),转换成8进制;

    8进制转换成二进制:用3位二进制代码替换每一位8进制;

     

    二进制转行成16进制:从右向左,每四位一组(不足四位的在左侧补0),转换成16进制;

    16进制转换成二进制:用四位二进制代码替换每一位16进制;

     

    负数的16进制的表示方法:首先应该将其表示成二进制形式,然后变反码,再变补码,然后把补码每四位转换成16进制。

    以-1来说:将-1表示成4位二进制数(求补码),就是1111(此时将1111当成有符号数),然后直接化16进制数;-1的一位有符号16进制数就是 F. -10的8位有符号二进制数为11110110(补码表示),化为2位有符号的16进制数FA.

    如何判断一个16进制数是正是负? 看有没有指明这个16进制数是否为有符号数,如果题目说明为无符号数,则表示正数. 如果为有符号数,就要判断符号的正负:将16进制数的最高位化为4位二进制数,如果所化的二进制数的最高位为1就表示负数,为0就表示正数. 例 FA 为有符号的16进制数,F为FA的最高位,化为二进制数为1111,而1111的最高位为1,就表示FA是个负数.FA化为10进制数就为 -10

    负数的二进制表示:最左侧位0表示整数,1表示负数;

    溢出:https://www.cnblogs.com/Jamesjiang/p/8947252.html

     

    ***常规使用:

    为什么要byte要和0xff进行按位与运算?

    当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,例如补码11111111的十进制数为-1,转换为int时变为11111111 11111111 11111111 11111111,即0xffffffff但是这个数是不对的,这种补位就会造成误差。和0xff相与后,高24比特就会被清0了,结果就对了。

     

    C语言提供了六种位运算符:

    运算符 & | ^ ~ << >>

    说明 按位与 按位或 按位异或 取反 左移 右移

     

    *****************按位与运算(&)

    一个比特(Bit)位只有 0 和 1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。例如1&1为 1,0&0为 0,1&0也为 0,这和逻辑运算符&&非常类似。

     

    C语言中不能直接使用二进制,&两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都是以二进制形式存储,&就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理。

     

    例如,9 & 5可以转换成如下的运算:

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    & 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  (1 在内存中的存储)

    也就是说,按位与运算会对参与运算的两个数的所有二进制位进行&运算,9 & 5的结果为 1。

     

    又如,-9 & 5可以转换成如下的运算:

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    & 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -9 & 5的结果是 5。

    &是根据内存中的二进制位进行运算的,而不是数据的二进制形式;其他位运算符也一样。以-9&5为例,-9 的在内存中的存储和 -9 的二进制形式截然不同:

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    -0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (-9 的二进制形式,前面多余的 0 可以抹掉)

     

    按位与运算通常用来对某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以进行n & 0XFFFF运算(0XFFFF 在内存中的存储形式为 0000 0000 -- 0000 0000 -- 1111 1111 -- 1111 1111)。

     

    【实例】对上面的分析进行检验。

    #include <stdio.h>

    int main(){

        int n = 0X8FA6002D;

        printf("%d, %d, %X\n", 9 & 5, -9 & 5, n & 0XFFFF);

        return 0;

    }

    运行结果:

    1, 5, 2D

     

     

    **********按位或运算(|)

     

    参与|运算的两个二进制位有一个为 1 时,结果就为 1,两个都为 0 时结果才为 0。例如1|1为1,0|0为0,1|0为1,这和逻辑运算中的||非常类似。

     

    例如,9 | 5可以转换成如下的运算:

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    |   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1101  (13 在内存中的存储)

    9 | 5的结果为 13。

     

    又如,-9 | 5可以转换成如下的运算:

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    |   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    -9 | 5的结果是 -9。

     

    按位或运算可以用来将某些位 置 为1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以进行n | 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

     

    【实例】对上面的分析进行校验。

    #include <stdio.h>

    int main(){

        int n = 0X2D;

        printf("%d, %d, %X\n", 9 | 5, -9 | 5, n | 0XFFFF0000);

        return 0;

    }

    运行结果:

    13, -9, FFFF002D

     

    *********按位异或运算(^)

     

    参与^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0^1为1,0^0为0,1^1为0。

     

    例如,9 ^ 5可以转换成如下的运算:

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    ^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1100  (12 在内存中的存储)

    9 ^ 5的结果为 12。

     

    又如,-9 ^ 5可以转换成如下的运算:

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    ^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

    -----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0010  (-14 在内存中的存储)

    -9 ^ 5的结果是 -14。

     

    按位异或运算可以用来将某些二进制位反转。例如要把 n 的高 16 位反转,保留低 16 位,可以进行n ^ 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

     

    【实例】对上面的分析进行校验。

    #include <stdio.h>

    int main(){

        unsigned n = 0X0A07002D;

        printf("%d, %d, %X\n", 9 ^ 5, -9 ^ 5, n ^ 0XFFFF0000);

        return 0;

    }

    运行结果:

    12, -14, F5F8002D

     

     

    **************取反运算(~)

     

    取反运算符~为单目运算符,右结合性,作用是对参与运算的二进制位取反。例如~1为0,~0为1,这和逻辑运算中的!非常类似。。

     

    例如,~9可以转换为如下的运算:

    ~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    -----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0110  (-10 在内存中的存储)

    所以~9的结果为 -10。

     

    例如,~-9可以转换为如下的运算:

    ~ 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000  (9 在内存中的存储)

    所以~-9的结果为 8。

     

    【实例】对上面的分析进行校验。

    #include <stdio.h>

    int main(){

        printf("%d, %d\n", ~9, ~-9 );

        return 0;

    }

    运行结果:

    -10, 8

     

     

    ************左移运算(<<)

     

    左移运算符<<用来把操作数的各个二进制位全部左移若干位,高位丢弃,低位补0。

     

    例如,9<<3可以转换为如下的运算:

    << 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0100 1000  (72 在内存中的存储)

    所以9<<3的结果为 72。

     

    又如,(-9)<<3可以转换为如下的运算:

    << 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    -----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1011 1000  (-72 在内存中的存储)

    所以(-9)<<3的结果为 -72

     

    如果数据较小,被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方

     

    【实例】对上面的结果进行校验。

    #include <stdio.h>

    int main(){

        printf("%d, %d\n", 9<<3, (-9)<<3 );

        return 0;

    }

     

    运行结果:

    72, -72

     

    ************右移运算(>>)

     

    右移运算符>>用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补 0 或 1。如果数据的最高位是 0,那么就补 0;如果最高位是 1,那么就补 1。

     

    例如,9>>3可以转换为如下的运算:

    >> 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

    -----------------------------------------------------------------------------------

    0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  (1 在内存中的存储)

    所以9>>3的结果为 1。

     

    又如,(-9)>>3可以转换为如下的运算:

    >> 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

    -----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110  (-2 在内存中的存储)

    所以(-9)>>3的结果为 -2

     

    如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但被移除的位中经常会包含 1)。

     

    【实例】对上面的结果进行校验。

    #include <stdio.h>

    int main(){

        printf("%d, %d\n", 9>>3, (-9)>>3 );

        return 0;

    }

    运行结果:

    1, -2

     

    =============原码、反码、补码:

     

    一、什么是原码、反码和补码

      在计算机内部存储的带符号数都是以补码形式存储,用补码形式进行运算的。以整型数为例,且假定字长为8位。

      1、原码

      整数X的原码是指:其符号位为0表示正,为1表示负;其数值部分就是X的绝对值的二进制数。X的原码通常用【X】原表示。如:

      【+100】原=01100100 【+0】原=00000000

      【-100】原=11100100 【-0】原=10000000注意:在原码中,零有两种表示形式。

      原码表示法简单易懂,与真值(带符号数本身)转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。

      2、反码

      X的反码是指:对于正数,反码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反(1变0,0变1)。X的反码通常用【X】反来表示。如

      【+100】反=01100100 【+0】反=00000000

      【-100】反=10011011【-0】反=11111111

      注意:在反码中,零也有两种表示形式。

      反码运算也不方便,通常用来作为求补码的中间过渡。

      3、补码

      X的补码是指:对于正数,补码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反后在最低位加1。X的补码通常用【X】补来表示,实际上,【X】补=【X】反+1。如:

      【+100】补=01100100 【+0】补=00000000

      【-100】补=10011100 【-0】补=00000000

      注意:在补码中,零有唯一的编码,【+0】补=【-0】补=00000000。

      补码运算简单方便,符号位可以作为数据的一位参与运算,不必单独处理;二进制的减法可用其补码的加法来实现,简化了硬件电路。

    1. 负数原码和反码的相互转化   负数原码转化为反码:符号位不变,数值位按位取反。   如: 原码 1100 0010 反码 1011 1101   负数反码转化为原码:符号位不变,数值位按位取反。 反码 1011 1101 原码 1100 0010  2. 负数原码和补码的相互转化   负数原码转化为补码:符号位不变,数值位按位取反,末尾加一。 原码 1100 0010 反码 1011 1101 //符号位不变,数值位按位取反 补码 1011 1110 //末尾加1   负数补码转化为原码:符号位不变,数值位按位取反,末尾加1。 补码 1011 1110 1100 0001 //符号位不变,数值位按位取反 原码 1100 0010 //末尾加1  3.负数反码和补码的相互转化   负数反码转化为补码:末尾加1。 反码 1011 1101 补码 1011 1110   负数补码转化为反码:末尾减1(注意,此处的反码是指原码的反码)。 补码 1011 1110 原码的反码   1011 1101 //减法 借位

      补码转换为原码:符号位不变,数值位按位取反,末位再加1。即补码的补码等于原码.

    一个汉子等于两个字节,包括中文标点符号;一个英文占一个字节;

    两个16进制数等于一个字节;一个字节占8位,最多表示-128~127或者0~255;

    很多计算,都使用0x这样的16进制进行运行: 如 0xF的二进制为 1111 ,即四个1。 0xFF的二进制为 1111 1111 ,即8个1的二进制形式 每多一个F就是多一个4位的1111。 最多8个F。

    ***16进制的反码和补码:

    将一个十六进制整数按位取反并加 1,就生成了它的补码。一个简单的十六进制数字取反方法就是用 15 减去该数字。下面是一些十六进制数求补码的例子:

    6A3D --> 95C2 + 1 --> 95C3 95C3 --> 6A3C + 1 --> 6A3D

    通过检查十六进制数的最高有效(最高)位,就可以知道该数是正数还是负数。如果最高位 ≥ 8,该数是负数;如果最高位 ≤ 7,该数是正数。比如,十六进制数 8A20 是负数,而 7FD9 是正数.

     

    转载请注明原文地址: https://ju.6miu.com/read-664624.html

    最新回复(0)