当前位置:   article > 正文

【Java】原码反码补码_java反码补码原码

java反码补码原码

一、概念

原码:十进制数据的二进制表示形式,最左边是符号位,0为正,1为负。

反码:正数的补码反码是其本身,负数的反码是符号位保持不变,其余位取反。

补码:整数的补码是其本身,负数的补码是在其反码的基础上加1。


二、原码

原码:十进制数据的二进制表示形式,最左边是符号位,0为正,1为负。

例如:56,下图转为二进制就是它的原码

image-20240330174321153

其中左边的第一位是它的符号位,0为正,1为负,后面才是具体的数值,称为数值位。

image-20240330174250270

在计算机中,一个0或者一个1,我们称之为一个bit,中文名字叫比特位。比特币和比特位它们两个之间没有任何关系,就是名字比较像而已。

但是一个bit能表示的数据太少了,所以我们会把8个bit分为一组,叫为一个字节,而一个字节是我们计算机中最小的存储单元。

那么一个字节最大值能取到多少?第一个符号位一定是0,后面的7位肯定是1。因为二进制最多只能到1,不能到2,因为到2就会逢二进一。所以一个字节的最大值是0后面跟着7个1。把它转为十进制,那就是正的127

image-20240330174711815

那么一个字节最小值能取到多少?第一个符号位肯定是1,表示负数,后面的数字是不能为0的,如果为0,这个就是-0了,-0也是0。我们应该将后面的值都变成1才行。后面的7个1转为十进制,就是127,组合在一起就是-127。

image-20240330174939275

在原码当中,如果是正数计算,结果不会有任何的问题。例如在0的基础上加1,直接将右边的0变成1就行了。

image-20240330175233480


原码的弊端

左边的第一位为1,表示负数,后面7个都是0,转成十进制后,就是-0,-0其实也是0。现在我在它的基础上加个1。

现在,我要在这个基础上加1,正常来说是1,但是下面这个二进制转成十进制后,结果是-1 。

image-20240330175544781

现在以-1为基础,再加1。正确应该为0,但是通过这个二进制转换后的结果,实际确实-2。

image-20240330175657821

现在我在-2的基础上继续加1,正确来讲结果应该为-1,但是现在通过这个二进制转换后的十进制,实际为-3。

image-20240330175741107

为什么会这样呢?我们讲数字归零,要说明这个,只需要结合数轴去理解。现在数据是0。

image-20240330175925426

现在是0要加1,我的想法是,应该向数轴的正方向去走一步。但是在这个前面,它有一个1,1表示符号。

image-20240330180126001

所以实际情况,它是反过来,往负的方向走了一步。

image-20240330180258041

总结:如果是负数计算,结果就会出错,实际运算的结果,跟我们预期的结果是相反的。所以在当时,有人就想了,如果将数轴反过来不就可以了吗?所以说就出现了反码。

image-20240331215255407


三、反码

反码:是为了解决原码不能计算负数的问题而出现的。

计算规则:正数的反码不变。负数的反码在原码的基础上,符号位不变,数值取反,0变1,1变0.

正数反码不变的原因是,在原码的计算中,正数的计算本身是没有问题的,既然都没有问题了,那还改它干嘛?只有负数的计算是跟原来的方向是反过来的,所以只需要将负数进行取反就可以了。

例如:我们以 -56 为例。首先要计算56的原码。

image-20240331215708588

56的原码是后面这段

image-20240331215824855

那现在是负数怎么办,那就将前面的符号位变成1就行了,组合在一起,那这个就是-56的原码。

那-56的反码是多少?负数的反码是符号位不变,数值位取反。0变1,1变0。

image-20240331220046035

那么反码可以解决负数相加的问题吗?我们来看一个例子,现在用 -56 + 1,正确来讲,它应该等于 -55,那我就想了,用反码计算,能得到 -55 吗?不知道的话就直接来计算一下:从最右边开始算,-56 的反码加1后,就需要往前进一。

image-20240401191700149

一共进位三次,最终就会变成下面这个数字。

image-20240401191826433

通过查询计算器,可知55的原码为:

image-20240401192030345

那如果现在想要得到 -55 ,符号位取反即可

image-20240401192111259

-55 的反码为:符号位不变,后面的数值按位取反,0变1,1变0。一旦变化之后,可以发现之前 -56 + 1 得到的反码和 -55 的反码 一模一样,因此利用反码,就可以完美解决负数计算的问题了。

image-20240401192219681


反码弊端

但这并没有结束,下面是 -1 到 -7 的反码,以-4为例,如果用原码去计算都是不正确的,所以就出现了右边的反码。

反码计算负数都是没有问题的,但是一直加到0的时候,0这时再 + 1,反码就会一直进位,超出范围就不管了,因为我们现在计算的是一个字节,所以最终得到的结果是 8 个 0 。

image-20240401192902324

其中8个0当中,左边的第一位是0,这时候它是正数,原码不变,还是8个0,所对应的十进制还是0。

因此,利用反码来计算,最终的结果跨0了,就会有1个误差。这是因为在反码中,0有两种表现形式,有一个是8个0的,还有一个是8个1的,跨0的时候在0这个地方蹦了两次。

image-20240401193121872


四、补码

补码就是将反码错开了一位,它把8个1规定为是-1的补码,而下面的7个1再加一个0,把它规定为是-2的补码,等等…以此类推

这样做就可以把0的两种表现形式给屏蔽掉了,在补码当中,0只有一种表现形式,那就是8个0。所以这也就是我们在计算补码的时候,需要使用反码 + 1的原因。

整数的原反补相同。

image-20240401194043689

例如下图 -4 + 5,利用补码计算,结果就是1!

image-20240401194527934

因此,补码完美解决了计算机当中正数和负数的计算问题。也正是因为这个原因,在计算机当中数字的存储还有运算,它都是以补码的形式来运行的。

补码还有一个小细节:因为补码是在反码的基础上 + 1计算得到的,所以 -127的补码如下图。

image-20240401194742629

这样来讲就会空出一个数:一个1后面有7个0。

image-20240401194930261

其实这个也很好理解,因为0原来在反码当中有两种表现形式,现在挨个错了1位,所以0这里就需要节省1个二进制表现形式的数,节省出来的就跑到了最前面。因此计算机就将 1 + 7个0 规定成特殊数字: -128。在一个字节中,-128是没有原码也没有反码的,只有补码的表现形式。当然这是不碍事的,因为计算机中数字的存储和计算都是以补码的形式来操作的,所以我们说,一个字节的取值范围是 -128 - 127


五、总结

原码:十进制数据的二进制表现形式,最左边是符号位,0为正,1为负。

原码的弊端:利用原码进行计算的时候,如果是正数完全没有问题。但是如果是负数计算,结果就会出错,实际运算的方向跟正确的运算方向是相反的,这个时候就出现了反码。

反码出现的目的:为了解决原码不能计算负数的问题而出现的。

反码的计算规则:正数的反码不变,负数的反码在原码的基础上,符号位不变。数值取反,0变1,1变0。

反码的弊端:负数运算的时候,如果不跨0,是没有任何问题的,但是如果结果跨0,跟实际结果会有1的偏差。这个时候就出现了最终的补码。

补码出现的目的:为了解决负数计算时跨0的问题而出现的。

补码的计算规则:正数的补码不变,负数的补码是在反码的基础上 + 1。另外补码还能多记录一个特殊的值 -128,该数据在1个字节下,没有原码和反码。

补码的注意点:计算机中的存储和计算都是以补码的形式进行的。


六、多学N招

在学完原反补后,就可以解释很多很多东西了,最后我们来多学N招。

1)不同类型的10有什么区别

在之前我们曾经说过,整数它有四种类型:byte、short、int、long。例如10,在不同类型下,它到底有什么区别呢?

byte在计算机中占1个字节,一共是8个比特位,8个0。short在计算机中是占用两个字节的,一共是16个比特位。int在内存中是占用4个字节,32个比特位。long在计算机中占8个比特位,一种64个比特位。对应的类型的10如下图:

image-20240401201715354


2)隐式转换

在之前,我们学习过类型转换。类型转换分为两种,其中一种就是隐式转换,那隐式转换的原理是什么样的呢?例如下段代码

public class Test {
    public static void main(String[] args) {
        byte a = 10; // 000 1010
        // 将取值范围小的赋值给取值范围大的,此时会触发隐式转换
        int b = a; // byte转为int类型时,直接在前面补0。0000 0000 0000 0000 0000 0000 0000 1010
        System.out.println(b);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3)强制转换

情况一

public class Test {
    public static void main(String[] args) {
        int a = 300; // 正数原反补相同,都为:0000 0000 0000 0000 0000 0001 0010 1100
        // 将 a 强制转为byte类型
        // 由于byte的取值范围是 -128 到 127,已经超出了取值范围。直接去掉前面多余的位,只剩下最后的8个比特位
        byte b = (byte)a; // 0010 1100,转为十进制为44
        System.out.println(b);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

情况二

public class Test {
    public static void main(String[] args) {
        int a = 200; // 正数原反补相同,都为:0000 0000 0000 0000 0000 0000 1100 1000
        byte b = (byte)a; // 去掉前面多余的位,留下8位为:1100 1000,此时要注意了,第一位变成了1,它是符号位,所以真正的数值变成了后面7位。并且在计算机中,数字的计算都是以补码的形式进行的,所以 1100 1000 还是补码形式。转为十进制,可以使用计算器计算
        System.out.println(b); // -56
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

程序员计算机使用技巧

使用计算机中 QWORD 可以调节想要的字节数

image-20240401203139206

选中第二个,然后不断切换 QWORD ,可以查看变化的字节数

image-20240401203331709

然后直接用鼠标点击0,它就会直接切换成1,切换后,这个就是对应的补码的形式。往上看,在十进制中,是-56。

image-20240401203539555

Java最终运行的结果,它还是会转为十进制的形式展示出来,所以上面强制转换中情况二的的结果是 -56。


4)其他的运算符

image-20240401203757270

逻辑与

现在逻辑与的作用并不是 true 和 false 了,而是一些数字。像这种计算方式在以后自己很少会去写,以后在看到一些源码的时候,有可能会看到一些这样的写法。所以在以后我们自己不用这么去写。

public class Test {
    public static void main(String[] args) {
        int a = 200;  // 0000 0000 0000 0000 0000 0000 1100 1000
        int b = 10; // 0000 0000 0000 0000 0000 0000 0000 1010
        // 由于在二进制中0为false,1为true。它们在计算的时候,就是每个比特位挨着去计算,如果都是true,结果才是true。
        System.out.println(a & b); // 0000 0000 0000 0000 0000 0000 0000 1000,转换为十进制后结果是8
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

逻辑或
public class Test {
    public static void main(String[] args) {
        int a = 200;  // 0000 0000 0000 0000 0000 0000 1100 1000
        int b = 10; // 0000 0000 0000 0000 0000 0000 0000 1010
        // 在计算的时候,只要有true,结果就是true。即只要有1,结果就是1
        System.out.println(a & b); // 0000 0000 0000 0000 0000 0000 1100 1010,转换为十进制后结果为202
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

PS:这种计算数字的逻辑与和逻辑或是没有短路的。


左移

将二进制向左移动,低位补0。

public class Test {
    public static void main(String[] args) {
        int a = 200;  // 0000 0000 0000 0000 0000 0000 1100 1000
        // 将 a 左移两次,可以看下图,左移完后,右边空了两位,所以低位补0。
        System.out.println(a << 2); 
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

image-20240401210300527

低位补0

image-20240401210408461

移完后,再转成十进制,就变成了800。移出去的这两位就不要了。

image-20240401210514891

公式:左移一位,就是乘以2。


右移

将二进制向右移动,最左侧叫做高位,高位补0或1。如果原来是正数,就补0;如果原来是负数,就补1。即:补的是符号位。

public class Test {
    public static void main(String[] args) {
        int a = 200;  // 0000 0000 0000 0000 0000 0000 1100 1000
        // 将 a 右移两次
        System.out.println(a >> 2); // 50
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

左边的最高位补符号位

image-20240401210847136

这里补的是0,移出去的位就不要了

image-20240401210938864

换算为十进制后得到的结果就为 50

公式:右移一次,相当于除以2


无符号右移

它跟右移是一回事,不过它在补符号的时候,不管原来数字是正是负,补的都是0。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/984498
推荐阅读
相关标签
  

闽ICP备14008679号