java中的原码,反码和补码

前言

今天ctrl+c代码的时候发现一个不认识的代码:

int v = src[i] & 0xFF;

于是就起了好奇心,想要搞清楚“& 0xFF”是什么东西。
于是查阅了一些资料(百度)
查到了一些其他的知识,在此做一个总结,并分享给大家我的简介

前提须知

  1. 众所周知,计算机使用的是二进制,例如:十进制的数“9”用0000 1001表示。
  2. 拿byte类型举例子来说,byte 数据类型是8位有符号的,以二进制补码表示的整数;表示范围是[-128, 127]

基础知识(了解下面这些更容易了解这三个概念)

解释这3个词之前,我先问个问题:

byte 数据类型是8位有符号的,以二进制补码表示的整数;

  • 请问这里的“有符号的”是指的什么?
  • 如何表示这个符号呢?

答案:

  • 这里的有符号的指的是正负号,如果没有符号,那就是从0~255这个取值区间,而不是-128 ~ 127了
  • 8位指的是8个0或1组成的数字串:例如:0101 1111,
    • 标记为黄色的这个也就是最高位这个为重点,称为符号位,这个数字为0时,后面7位数字代表了正数这个数字为1时,后面7位数字组成了负数(暂且忽略0这个特殊的数字)。
    • 后面7位数字是数值位代表了这个二进制到底是哪个数字。

举例:有这样3个byte类型的数字,分别是45,60,-70,他们在计算机存储时使用的是补码(下面会细讲什么是补码)形式的二进制,这3个数字换算成二进制的表示分别是:

	45 	= 0010 1101
	60 	= 0011 1100
	-60 = 1100 0100

有些小伙伴打开在线进制转换器就开始算了:
45和60没问题,但1100 0100 换算成10进制分明是196呀?

下面就正式引入原码,反码,补码

基础概念

1一句话讲概述什么是原码,反码,补码

孔乙己说过:计算机中的有符号数有三种表示方法,就像茴香豆的茴字有4种写法一样,虽然看起来不一样,但表示的是同一个数(字)

例如:

数字原码反码补码
450010 11010010 11010010 1101
600011 11000011 11000011 1100
-601011 11001100 00111100 0100

这里不需要看懂,你只需要知道不管原码,反码还是补码,描述的其实都是一个带符号(正号或者负号)的数字就行

2 什么是原码

(1)原码概念

原码就是把十进制数字的绝对值转化为8位(byte类型为8位,int类型则是36位)的二进制数字。
然后如果十进制的数字是正数,则原码首位为0,负数则原码首位为1

数字原码 (加粗位代表了正负号)
450 010 1101
600 011 1100
-601 011 1100

(2)十进制数字转化为原码

举个例子,分别求60-60的反码:	

	|-60| =  60  =  0011 1100
	因此60的原码就是0 011 1100
	
	因为-60是负数,所以最前面那个数字写作1
	结果是1 011 1100
	因此-60的原码就是1 011 1100

(3)原码转化为十进制

原码转化为十进制则是将数值位化为10进制,然后带上符号位代表的符号即可

例如:原码的1000 0011 化为十进制

  1. 先把后面7位000 0011化为10进制得到3
  2. 把首位的1变成符号加前面得到-3

这就解释了为什么明明是8位的byte类型,正数最大却只能显示到+127,因为除去了代表正负号的首位,只剩下7位数,最大值为111 1111,换算成十进制,只有127

3 什么是反码

(1)反码概念

如果你能理解了什么是原码,反码就很简单了

数字原码反码
450010 11010010 1101
600011 11000011 1100
-601011 11001100 0011

反码就是在原码的基础上,

  • 如果你是正数,则反码就是原码,反码==原码
  • 如果你是负数,符号位(最高位)保持不变,其他的数值位1变成0,0变成1

(2)十进制数字转化为反码

同样拿举个例子,分别求60-60的反码:	

	1	先求原码:
		|-60| =  60  =  0011 1100
		因此60的原码就是0011 1100
		因为-60是负数,所以最前面那个数字写作1
		结果是1 011 1100
	
	2	之后求负数-60的反码,在原码基础上符号位(最高位)保持不变,其他的数值位1变成00变成1
		得到-60的反码就是:11000011

(3)反码转化为十进制数字

如果知道一个补码的情况下,想求十进制数该怎么办呢?,只要再对这个数字进行取反并加上一的操作即可
例如:将反码1100 0011转化为10进制数字

  1. 首位是1,说明是负数,因此需要取反操作,如果首位是0,则可以省略下面的第二步
  2. 后7位的100 0011取反,得到011 1100
  3. 将第二步的数字化为十进制,得到60
  4. 加上第一步的符号位代表的正号或者符号就得到了结果"-60"

4 什么是补码

(1)补码概念

补码是比较重要的一个概念,因为使用补码可以把加法和减法统一做处理运算

数字原码反码补码
450010 11010010 11010010 1101
600011 11000011 11000011 1100
-601011 11001100 00111100 0100

如果你能理解原码和反码,那么补码就更好说了,补码就是在反码的基础上:

  • 如果你是正数,则原码和反码和补码是同一个东西,即
    正数原码 == 正数反码 == 正数补码
  • 如果你是负数,把负数的反码+1就得到了补码

(2)十进制数字转化为补码

例如:求-60的补码:

1-60的原码为:				1011 1100
2-60的反码为原码的数值位取反:1100 0011
2-60的补码为反码的数值位+11100 0100

答:-60的补码为1100 0100

(3)补码转化为十进制数字

例如:将反码1100 0100转化为10进制数字

  1. 首位是1,说明是负数,因此需要取反操作,如果首位是0,则可以省略下面的第二步和第三步
  2. 将1100 0100的后七位(数值位)取反,得到1011 1011
  3. 将第二步的数值加上1得到1011 1100
  4. 将数值位的数字化为十进制,得到60
  5. 将符号位代表的符号加到第四步的前面,得到了-60

这里着重注意-128这个数字,我也不知道为什么,1000 0000代表-128,
不过强行理解的话,首先先把128的二进制求出来:1000 0000,由于所求的-128是个负数,所以后面的7个0变成7个1,然后执行+1操作,又得到了1000 0000,这之后将-128的负号变成1放在首位上.
覆盖掉1000 0000最前面那个1,结果还是1000 0000
不过有个规律,就是一个负数的补码在去掉前面的符号位之后,剩下的数值位转化为一个正数,和之前的负数的绝对值相加正好等于2^(类型位数-1)
比如byte类型的-10,补码为1111 0110去掉符号位之后的111 0110化为2进制是118,加上原来-10的绝对值正好是2(byte位数-1) ,即2(8-1)=27=128,
如果是int类型的-10,补码则是1111 1111 1111 1111 1111 1111 1111 0110,
去掉符号位后是111 1111 1111 1111 1111 1111 1111 0110,换为2进制是2147483638,加上原来-10的绝对值正好是2147483648,也就是2(int位数-1),即2^(32-1)


具体使用的区别

这里引用一下百度百科和魔道电子老师的例子:

记得某个帖子里好像说过这样一句话(如果有错,请指出):

计算机中只有加法器,没有减法器


数学上计算1-1:

 1-1 = 1+(-1) = 0 //(没有问题的正常计算)

计算机上(使用原码计算):

 1-1 = 1+(-1)  =  0000 0001 + 1000 0001  =  1000 0010  
			但是1000 0010从原码解析为十进制时,首位是1,说明结果是负数
			后7位是000 0010,换成10进制得到2,带上符号则是-2
			很明显,这里出现了问题

计算机上(使用反码计算):

 1-1 = 1+(-1)  =  0000 0001 + 1111 1110  =  1111 1111  
			得到的结果1111 1111是反码,解析时首位为1,说明结果的符号是负数
			后7位反向得到原码即:111 1111  --> 000 0000,换成10进制结果为0
			但是带上符号则是-0,就出现了0000 0000 = 1000 0000  = 0(十进制)这种异常的情况

计算机上(使用补码计算):

 1-1 = 1+(-1)  =  0000 0001 + 1111 1111  =  1 0000 0000  
			得到的结果1 0000 0000是个9位的数字,byte只有8,因此最前面的1溢出,只剩下0000 0000
			因为0000 0000开头是0,因此这是个正数, 后面的7个数字000 0000换成十进制则是0
			因此结果为0

因此,计算机上存储数据是以补码的形式进行存储
举例:-10-120=(-10)+(-120)

 byte d = -10;
 byte e = -120;
 System.out.println("d+e=" + (byte) (d+e))

补码计算就是(1111 0110)+(1000 1000)=1 0111 1110,由于是byte类型,只取了后8位,则是0111 1110,换算成十进制就是126这种不合常理的结果

额外补充

"模"就是一个计量单位的范围,比如钟表,在不考虑日期的情况下,时间倒退12小时,前进12小时或者前进24小时,最后我们所在的时间点还是一样的。因此对于12小时制的钟表,模就是12,对于24小时制的钟表,模就是24。
在计算机中的体现就是作为8bit的二进制数字,最大能表示的模为2^8,也就是256(-128~127)。

如果觉得比较抽象,请看这一段 :8bit可以看做从0~255的一个钟表,只不过这个表有点奇怪,根据0000 0000~数到1111 1111,也就是首位为0时第一阶段从0走到127,接着第二阶段首位为1从-128一直走到-1,两个阶段一个循环,共计256刻度。第0小时在第0的位置上,第255小时在-1的位置上。当进行到256,512,768小时的时候,就开始新的循环,和第一个小时是一样的数值,都是指向0。只要除以256这个模得到的余数相同,对于byte类型来说,就是同一个数字,因为多出的位数都被溢出了。

 public static void main(String[] args) {
        byte a = 127;//此时钟表刻度为127,二进制为:0111 1111
        System.out.println(a);//输出127
        System.out.println(++a);//输出-128 进入2阶段之后二进制为:1000 0000,正好对应-128的二进制编码
        System.out.println(++a);//接着输出-127,对应二进制为1000 0001
    }

补码计算本质上还是利用了模的同余,通俗举个例子:如果要求在0~100之间做加减法,超过100的从1开始接着加,比如99+1+2=100+2=0+2=2,小于0的从100接着减,比如2-2-3=0-3=100。反正得到的结果必须在这个区间之内。在这样的条件限制之下,模就是100,我们发现50+96和50-4其实是同样的结果,96与4在这里其实就是对模100同余,就像是上述的补码一样

结言

我好像忘了最开始是想搞清楚& 0xFF这东西了…如果觉得不错,记得点一下赞,如果有错误的地方,还请指出

在下第一次根据自己见解写博客分享,难免有错误纰漏,如果有错误,还请指出.在下不才,但会虚心接受错误并改正,如果有意见,可以直接在评论区评论
允许搬运,但需注明出处链接,并将此信息置于醒目处

本文参考了:
同余定理
补码
有了二进制和十进制,为什么还要十六进制,它有什么用
原码反码和补码是什么意思,为什么计算机用补码存储数据
计算机加法器电路为什么能实现减法,根本原因不难理解

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值