赞
踩
原码:十进制数据的二进制表示形式,最左边是符号位,0为正,1为负。
反码:正数的补码反码是其本身,负数的反码是符号位保持不变,其余位取反。
补码:整数的补码是其本身,负数的补码是在其反码的基础上加1。
原码:十进制数据的二进制表示形式,最左边是符号位,0为正,1为负。
例如:56,下图转为二进制就是它的原码
其中左边的第一位是它的符号位,0为正,1为负,后面才是具体的数值,称为数值位。
在计算机中,一个0或者一个1,我们称之为一个bit,中文名字叫比特位。比特币和比特位它们两个之间没有任何关系,就是名字比较像而已。
但是一个bit能表示的数据太少了,所以我们会把8个bit分为一组,叫为一个字节,而一个字节是我们计算机中最小的存储单元。
那么一个字节最大值能取到多少?第一个符号位一定是0,后面的7位肯定是1。因为二进制最多只能到1,不能到2,因为到2就会逢二进一。所以一个字节的最大值是0后面跟着7个1。把它转为十进制,那就是正的127
那么一个字节最小值能取到多少?第一个符号位肯定是1,表示负数,后面的数字是不能为0的,如果为0,这个就是-0了,-0也是0。我们应该将后面的值都变成1才行。后面的7个1转为十进制,就是127,组合在一起就是-127。
在原码当中,如果是正数计算,结果不会有任何的问题。例如在0的基础上加1,直接将右边的0变成1就行了。
左边的第一位为1,表示负数,后面7个都是0,转成十进制后,就是-0,-0其实也是0。现在我在它的基础上加个1。
现在,我要在这个基础上加1,正常来说是1,但是下面这个二进制转成十进制后,结果是-1 。
现在以-1为基础,再加1。正确应该为0,但是通过这个二进制转换后的结果,实际确实-2。
现在我在-2的基础上继续加1,正确来讲结果应该为-1,但是现在通过这个二进制转换后的十进制,实际为-3。
为什么会这样呢?我们讲数字归零,要说明这个,只需要结合数轴去理解。现在数据是0。
现在是0要加1,我的想法是,应该向数轴的正方向去走一步。但是在这个前面,它有一个1,1表示符号。
所以实际情况,它是反过来,往负的方向走了一步。
总结:如果是负数计算,结果就会出错,实际运算的结果,跟我们预期的结果是相反的。所以在当时,有人就想了,如果将数轴反过来不就可以了吗?所以说就出现了反码。
反码:是为了解决原码不能计算负数的问题而出现的。
计算规则:正数的反码不变。负数的反码在原码的基础上,符号位不变,数值取反,0变1,1变0.
正数反码不变的原因是,在原码的计算中,正数的计算本身是没有问题的,既然都没有问题了,那还改它干嘛?只有负数的计算是跟原来的方向是反过来的,所以只需要将负数进行取反就可以了。
例如:我们以 -56
为例。首先要计算56的原码。
56的原码是后面这段
那现在是负数怎么办,那就将前面的符号位变成1就行了,组合在一起,那这个就是-56的原码。
那-56的反码是多少?负数的反码是符号位不变,数值位取反。0变1,1变0。
那么反码可以解决负数相加的问题吗?我们来看一个例子,现在用 -56 + 1
,正确来讲,它应该等于 -55
,那我就想了,用反码计算,能得到 -55
吗?不知道的话就直接来计算一下:从最右边开始算,-56
的反码加1后,就需要往前进一。
一共进位三次,最终就会变成下面这个数字。
通过查询计算器,可知55的原码为:
那如果现在想要得到 -55
,符号位取反即可
-55
的反码为:符号位不变,后面的数值按位取反,0变1,1变0。一旦变化之后,可以发现之前 -56 + 1
得到的反码和 -55
的反码 一模一样,因此利用反码,就可以完美解决负数计算的问题了。
但这并没有结束,下面是 -1 到 -7 的反码
,以-4为例,如果用原码去计算都是不正确的,所以就出现了右边的反码。
反码计算负数都是没有问题的,但是一直加到0的时候,0这时再 + 1,反码就会一直进位,超出范围就不管了,因为我们现在计算的是一个字节,所以最终得到的结果是 8 个 0 。
其中8个0当中,左边的第一位是0,这时候它是正数,原码不变,还是8个0,所对应的十进制还是0。
因此,利用反码来计算,最终的结果跨0了,就会有1个误差。这是因为在反码中,0有两种表现形式,有一个是8个0的,还有一个是8个1的,跨0的时候在0这个地方蹦了两次。
补码就是将反码错开了一位,它把8个1规定为是-1的补码,而下面的7个1再加一个0,把它规定为是-2的补码,等等…以此类推
这样做就可以把0的两种表现形式给屏蔽掉了,在补码当中,0只有一种表现形式,那就是8个0。所以这也就是我们在计算补码的时候,需要使用反码 + 1的原因。
整数的原反补相同。
例如下图 -4 + 5
,利用补码计算,结果就是1!
因此,补码完美解决了计算机当中正数和负数的计算问题。也正是因为这个原因,在计算机当中数字的存储还有运算,它都是以补码的形式来运行的。
补码还有一个小细节:因为补码是在反码的基础上 + 1计算得到的,所以 -127的补码如下图。
这样来讲就会空出一个数:一个1后面有7个0。
其实这个也很好理解,因为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招。
在之前我们曾经说过,整数它有四种类型:byte、short、int、long。例如10,在不同类型下,它到底有什么区别呢?
byte在计算机中占1个字节,一共是8个比特位,8个0。short在计算机中是占用两个字节的,一共是16个比特位。int在内存中是占用4个字节,32个比特位。long在计算机中占8个比特位,一种64个比特位。对应的类型的10如下图:
在之前,我们学习过类型转换。类型转换分为两种,其中一种就是隐式转换,那隐式转换的原理是什么样的呢?例如下段代码
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);
}
}
情况一
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);
}
}
情况二
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
}
}
使用计算机中 QWORD
可以调节想要的字节数
选中第二个,然后不断切换 QWORD
,可以查看变化的字节数
然后直接用鼠标点击0,它就会直接切换成1,切换后,这个就是对应的补码的形式。往上看,在十进制中,是-56。
Java最终运行的结果,它还是会转为十进制的形式展示出来,所以上面强制转换中情况二的的结果是 -56。
现在逻辑与的作用并不是 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
}
}
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
}
}
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);
}
}
低位补0
移完后,再转成十进制,就变成了800。移出去的这两位就不要了。
公式:左移一位,就是乘以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
}
}
左边的最高位补符号位
这里补的是0,移出去的位就不要了
换算为十进制后得到的结果就为 50
公式:右移一次,相当于除以2
它跟右移是一回事,不过它在补符号的时候,不管原来数字是正是负,补的都是0。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。