• Java基础-原码反码补码


      注意,我们这里举列的原码和反码只是为了求负数的补码,在计算机中没有原码,反码的存在,只有补码。

    一.原码

    1>.正数的原码就是它的本身

      假设使用一个字节存储整数,整数10的原码是:0000 1010

    2>.负数用最高位是1表示负数

      假设使用一个字节存储整数,整数-10的原码是:1000 1010

    二.反码

    1>.正数的反码跟原码一样

      假设使用一个字节存储整数,整数10的反码是:0000 1010

    2>.负数的反码是负数的原码按位取反(0变1,1变0),符号位不变

      假设使用一个字节存储整数,整数-10的反码是:1111 0101

    三.补码(再次强调,整数的补码才是在计算机中的存储形式。)

    1>.正数的补码和原码一样

      假设使用一个字节存储整数,整数10的补码是:0000 1010(第三次强调:这一串是10这个整数在计算机中存储形式)

    2>.负数的补码是负数的反码加1

      假设使用一个字节存储整数,整数-10的补码是:1111 0110(第三次强调:这一串是-10这个整数在计算机中存储形式)

    四.在计算机中,为什么不用原码和反码,而是用补码呢?

      因为在使用原码,反码在计算时不准确,使用补码计算时才准确。

    1>.使用原码计算10-10

             0000 1010  (10的原码)

        +        1000 1010   (-10的原码)

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

             1001 0100  (结果为:-20,很显然按照原码计算答案是否定的。)

    2>.使用反码计算10-10

          0000 1010  (10的反码)

        +   1111 0101  (-10的反码)

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

          1111 1111  (计算的结果为反码,我们转换为原码的结果为:1000 0000,最终的结果为:-0,很显然按照反码计算答案也是否定的。)

    3>.使用补码计算10-10

          0000 1010  (10的补码)

       +   1111  0110  (-10的补码)

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

          1 0000 0000  (由于我们这里使用了的1个字节存储,因此只能存储8位,最高位(第九位)那个1没有地方存,就被丢弃了。因此,结果为:0)

    五.小试牛刀

      有了上面的案例,接下来,我们来做几个小练习吧,分别计算以下补码表示的十进制数字是多少呢?

    1>.0b0000 1111

      相信这个数字大家异口同声的就能说出它的答案是:15(因为正数的补码和原码一样)

    2>.0b1111 1111

      计算过程:0b1111 1111(补码)------>0b1111 1110(反码)------>0b1000 0001(原码)

      将其换算成原码之后就可以得到最后的结果为:-1

    3>.0b1111 0000

      计算过程:0b1111 0000(补码)------>0b1110 1111(反码)------>0b10010000(原码)

      将其换算成原码之后就可以得到最后的结果为:-16

    4>.0b1000 0001

      计算过程:0b1000 0001(补码)------>0b1000 0000(反码)------->0b1111 1111(原码)

      将其换算成原码之后就可以得到最后的结果为:-127

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

    最近在tcp的基础上写一个自定义的协议,处理拆包粘包的时候发现一个情况

    数据是以字节流的形式在tcp中传输,所以,大于一个字节的数据类型,都要转为byte[] 的形式

    以int类型举例,在java中一个int类型的数据占4个字节,也就是需要new byte[4]

            int a = 9071;
            byte[] bytes = new byte[4];
            bytes[0] = (byte) (a >> 24 ); //  拿到最高位的8位
            bytes[1] = (byte) (a >> 16 );
            bytes[2] = (byte) (a >> 8 );
            bytes[3] = (byte) (a);

    java中的数据类型都是有符号类型,也就是说区分正负的

    一个字节=8bit,也就是说一个 byte 可以存放8个 0 或者 1

    1 的二进制原码是 0 000 000 1
    -1 的二进制原码是 1 000 000 1,补码是 1111 1111

    二进制的最高位为符号位,所以byte 的取值范围为-128~127,(-2^7~2^7-1)

    在 Java 中,是采用补码来表示数据的。

    正数的补码和原码相同,负数的补码是在原码的基础上各位取反然后加 1。

    譬如:int a = -29 的二进制表示为11111111111111111111111111100011

    int 32字节,先取a的的绝对值求原码,29 的原码为00011101

    不足4字节,高位补24个0

    00000000000000000000000000011101

    再求反码

    11111111111111111111111111100010

    +1
    于是得到了-29的二级制补码

    11111111111111111111111111100011

    通过上面的代码看,int 在tcp传输的时候要转换为byte[], int a = -29; 一共4个字节32位,于是byte[4] 每一个小标存储一个字节也就是8位,写到这里我们发现还是用不上&0xff,不要着急,接下来就用上了

    于是,我们发现int 29 被装进 byte[] 的时候,成了这个样子:

    下标0表示的是前8位,
    下标1表示的是8-16位,
    下标2表示的是16-24位,
    下标3表示的是24-32位

    也很容易理解,因为-1的补码就是 11111111 ,所以这4个byte的二进制组合起来就是int -29 的二进制

    image.png

    你把数据拆开了还要把它组合起来啊,也就是byte[] 转 int

    分解问题就是单个byte 转 int

    byte 八个bit位,int 32个bit位,byte为负数的时候,需要把高24位的全部置为0.保持低八位的一致性,不然得到的int就成了另一个数字

    0xff 是16进制,也就是255 二进制也就是 1111 1111
    补到32位也就是 这里是24个0 这里是八个1
    一个负数的byte & 0xff的时候,高24位就成了0,保证了一致性
    然而正数的补码还是它自己,不受影响,虽然只有负数的时候才需要 &0xff,但不至于再判断一次去吧?

          byte a = -1;// 1111 1111
            short b = -1;//1111 1111 1111 1111
            int c = -1;//  1111 1111 1111 1111 1111 1111 1111 1111
            System.out.println(a);//-1
            System.out.println(b);//-1
            System.out.println(c);//-1
    
            System.out.println(a&0xff);// 1111 1111&1111 1111  =255
            System.out.println(a&0xffff);//(byte –> int 就是由8位变 16 位 高8位全部补1,如果是正数补0) 1111 1111 1111 1111&1111 1111 1111 1111  =65535
            System.out.println(a&0xffffffffL);//(byte –> int 就是由8位变 32 位 高24位全部补1,如果是正数补0)
            // 1111 1111 1111 1111 1111 1111 1111 1111&1111 1111 1111 1111 1111 1111 1111 1111  =4294967295
    
            System.out.println(b&0xff);//255
            System.out.println(b&0xffff);//65535
            System.out.println(b&0xffffffffL);//4294967295
    
            System.out.println(c&0xff);//255
            System.out.println(c&0xffff);//65535
            System.out.println(c&0xffffffffL);//4294967295

    <<表示左移移,不分正负数,低位补0; 

    注:以下数据类型默认为byte-8位

    左移时不管正负,低位补0

    正数:r = 20 << 2

      20的二进制补码:0001 0100

      向左移动两位后:0101 0000

             结果:r = 80

    负数:r = -20 << 2

      -20 的二进制原码 :1001 0100

      -20 的二进制反码 1110 1011

      -20 的二进制补码 :1110 1100

      左移两位后的补码:1011 0000

            反码:1010 1111

            原码:1101 0000

            结果:r = -80

    >>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;

    注:以下数据类型默认为byte-8位

    正数:r = 20 >> 2

      20的二进制补码:0001 0100

      向右移动两位后:0000 0101

           结果:r = 5

    负数:r = -20 >> 2

      -20 的二进制原码 :1001 0100

      -20 的二进制反码 :1110 1011

      -20 的二进制补码 :1110 1100 

      右移两位后的补码:1111 1011 

            反码:1111 1010

            原码:1000 0101

            结果:r = -5

    >>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0

    正数: r = 20 >>> 2

        的结果与 r = 20 >> 2 相同;

    负数: r = -20 >>> 2

    注:以下数据类型默认为int 32位

      -20:源码:10000000 00000000 00000000 00010100

        反码:11111111  11111111   11111111   11101011

        补码:11111111  11111111   11111111   11101100

        右移:00111111  11111111   11111111   11111011

        结果:r = 1073741819

    一辈子很短,努力的做好两件事就好;第一件事是热爱生活,好好的去爱身边的人;第二件事是努力学习,在工作中取得不一样的成绩,实现自己的价值,而不是仅仅为了赚钱;
  • 相关阅读:
    Alpha冲刺(2/10)——2019.4.25
    Alpha冲刺(1/10)——2019.4.24
    Alpha冲刺——序言篇(任务与计划)
    团队作业第六次—团队Github实战训练
    团队第四次作业答辩——反思与总结
    团队作业第五次—项目系统设计与数据库设计
    项目Alpha冲刺--6/10
    项目Alpha冲刺--5/10
    项目Alpha冲刺--4/10
    项目Alpha冲刺--3/10
  • 原文地址:https://www.cnblogs.com/linjiaxin/p/14870850.html
Copyright © 2020-2023  润新知