原码、反码、补码等学习笔记

以前就学过这一块的知识,但是基本忘了......借着学习Java的过程中把这些知识记录下,好记性不如烂笔头。

符号位

计算机中用二进制中的最高位(最左边)来表示这个数是负数还是正数,也就是符号位,其中0表示正数,1表示负数。当然,也有没有符号位的数,他们的最高位表示的是数字本身的值。故可以把二进制数分为有符号数(signed,ps:这也就是C#中sbytes的含义)和无符号数(unsigned)
比如:100010008位有符号数中表示的是为一个负数,在8位无符号数中表示136
有些编程语言,比如Java,它所有和数字相关的数据类型都是有符号的;而有些编程语言,比如C、C#,他有诸如 unsigned int 这种无符号位的数据类型。

溢出

在数学中,数字可以有无穷大,无穷小。可是,在计算机中,数字都是有长度的;无论是何种数据类型,都有一个上限和下限。
对于n位的整数类型,符号位是1,其他位都是0,就是该类型是最小值-(2^(n-1)),注意不是0,所有位都是0(符号位也是0)时才为0;符号位是0,其他位都是1,就是该类型的最大值2^(n-1)-1。一旦某个数字超过了这个范围,就会发生溢出。如果小于最小值,叫下溢出。如果大于最大值,叫上溢出
JavaC#中,int类型是32位,表示-2^31 ~ 2^31 - 1之间的任意整数(最高位是符号位),表达式(2^31 - 1) + 1就超过了int能表达的最大整数,那么这个表达式的结果是什么呢?答案是:-2^31,这就是溢出

    // 2^31-1 = 2147483647,-2^31= -2147483648 
    System.out.println(2147483647+1);  // -2147483648
    System.out.println(-2147483648-1); // 2147483647
    var num = int.MaxValue;
    Console.WriteLine(num + 1 == int.MinValue); // True

    try
    {
        // unchecked 禁止溢出检测,默认是此规则
        Console.WriteLine(unchecked(num + 1 == int.MinValue));// True
        // checked 对整型运算做溢出检测
        Console.WriteLine(checked(num + 1 == int.MinValue));
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex); // System.OverflowException: 算术运算导致溢出。
    }

原码

原码就是我们看到的二进制的原始表示。下文案例中数字均以int类型为例,不再复述!

+8 = 0x00_00_00_08[原] = 00000000_00000000_00000000_00001000[原]

-8 = 0x80_00_00_08[原] = 10000000_00000000_00000000_00001000[原]

可以看到负数的原码就是把它的相反数的原码的符号位改为 1

反码

正数的反码就是其本身,负数的反码就是除了符号位外其他位取反取反就是0变1 1变0

+8 = 0x00_00_00_08[原] = 0x00_00_00_08[反] = 00000000_00000000_00000000_00001000[反]

-8 = 0x80_00_00_08[原] = 0xFF_FF_FF_F7[反] = 11111111_11111111_11111111_11110111[反]

补码

正数的补码就是其本身,负数的补码 = 其反码 + 1 计算机中数字就是以补码的存储的。扩展1扩展2

+8 = 0x00_00_00_08[原] = 0x00_00_00_08[反] = 0x00_00_00_08[补] = 00000000_00000000_00000000_00001000[补]

-8 = 0x80_00_00_08[原] = 0xFF_FF_FF_F7[反] = 0xFF_FF_FF_F8[补] = 11111111_11111111_11111111_11111000[补]

逻辑运算符

位与 &(And)

两个操作数进行按位与操作,仅当两个操作位都为1时结果位才为1。例:12&7=412&8=8

位或 |(Or)

两个操作数进行按位或操作,只要任意操作位为1时结果位就为1。例:12|7=1512|8=12

位异或 ^(Xor)

两个操作数进行按位异或操作,仅当两个操作位中有一个为1时结果位才为1。例:12^7=1112^8=4

位非 ~(Not)

对操作数的每个位都取反,这是一个一元操作符。例:~12= ?。我们知道计算机中数字以补码的形式存储,而正数的原码=补码=0...00001100,取反后的结果是1...11110011,这是负数的补码形式,我们直接看不出来他的值是多少,得转换成负数原码。我们知道负数原码转补码的过程是 原码=>(除符号位全部取反)=>反码=>+1=>补码,负数补码转原码就是把这个过程反过来,即补码=>-1=>反码=>除符号位全部取反=>原码。有了这个公式,可以算出反码为1...11110010,然后转为原码为1...00001101,最后转10进制为-1*2^3+2^2+2^0 = -13,不要忘了符号位是1(),流程如下:~12=00000000_00000000_00000000_00001100[补]=>取反=>11111111_11111111_11111111_11110011[补]=>11111111_11111111_11111111_11110010[反]=>10000000_00000000_00000000_00001101[原]=>-13
ps:对正负数取反有个简单的公式:~x=-(x+1)~12=-(12+1)~-13=-(-13+1)

移位运算符

逻辑移位

逻辑移位分逻辑左移和逻辑右移,移动中的空位补0。Java中有一个无符号右移(>>>),就是指的逻辑右移。C#中没有逻辑右移。Java和C#中都没有逻辑左移,因为逻辑左移的规则同算术左移。

算术移位

算术移位分算术左移和算术右移,算术左移同逻辑左移,逻辑右移和算术右移的区别是,逻辑右移左侧空位和符号位补0,算术右移符号位不变,左侧空位补符号位的值。Java和c#中都有这两种算术移位。
12 >> 3表示的含义就算术右移,数字12向右移3位。3 << 2指的是算术左移,数字3向左移2位。这里有个特点,箭头>>指向右边就是右移,箭头<<指向左边就是左移,箭头右边的数字是移动的位数

    System.out.println(12 >> 3);//0...00001100 >> 0...00000001=1
    System.out.println(12 >> 4);//0...00001100 >> 0...00000000=0
    System.out.println(12 >> 5);//0...00001100 >> 0...00000000=0
    //1...00001100[原]=>1...11110011[反]=>1...11110100[补] >> 1...11111110[补]=>1...11111101[反]=>1...00000010[原]
    System.out.println(-12 >> 3);//-2
    System.out.println(3 << 2);//0...00000011 >> 0...00001100=12
    System.out.println(3 << 3);//0...00000011 >> 0...00011000=24

    /*
    -7的原码为1000...00000111,反码为:1111...11111000,补码为:1111...11111001
    右移3位后变为:00011111_11111111_11111111_11111111 转10进制为:‭536870911‬
    用十六进制表示下流程:80_00_00_07[原]=>FF_FF_FF_F8[反]=>FF_FF_FF_F9[补]=>右移3位=>
    1F_FF_FF_FF[补]=>正数的 原码=反码=补码=>1F_FF_FF_FF[原]=>转10进制为:‭536870911‬
    */
    System.out.println(-7 >>> 3);//‭536870911‬
    Console.WriteLine(12 >> 3);//0...00001100 >> 0...00000001=1
    Console.WriteLine(12 >> 4);//0...00001100 >> 0...00000000=0
    Console.WriteLine(12 >> 5);//0...00001100 >> 0...00000000=0
    //1...00001100[原]=>1...11110011[反]=>1...11110100[补] >> 1...11111110[补]=>1...11111101[反]=>1...00000010[原]
    Console.WriteLine(-12 >> 3);//-2
    Console.WriteLine(3 << 2);//0...00000011 >> 0...00001100=12
    Console.WriteLine(3 << 3);//0...00000011 >> 0...00011000=24
posted @ 2019-09-19 16:26  寻己Tenleft  阅读(527)  评论(0编辑  收藏  举报