当前位置: 首页 > 科技观察

就这样,原码、反码、补码一起讨论,帮助学妹解决了困扰她三天的问题

时间:2023-03-17 01:44:06 科技观察

前言虽然逆码补码是一道简单的题,但确实很多人很长时间都没有理解和深入思考过。本文记录自己学习和理解的过程。刚好一个女学生问了这个问题。本文只讲原码、反码、补码。位操作可以看这篇文章。这个故事是一个真实的故事。前两天,我被一个小学生活活折磨死了。看不懂原码、反码、补码,就是看不懂。说了你还是不明白。我的上帝!不明白到底是二进制太难还是我太难?你不相信吗?拍张照片证明一下:她直接问我。二进制的符号位不参与计算?我怎么能听到?我一头雾水,哈哈哈,然后我跟他说我想参与计算,然后又问了一句:被她这么肯定的眼神我有点迷糊了,吓得我敲了一段代码验证一下结果没什么问题,巴拉巴拉又告诉了她。我觉得应该可以,结果凌晨1.30就出来了。。算了算了,这孩子没救了,别管,让她安静点。。。我也得安静点,sort出什么我一直在原来的代码中,搞不清自己的补码和自己的补码。记得刚学c++的时候:这玩意的代码不需要,不学操作系统和组成原理的时候:emumm,跳过跳过。考研时:不会深奥。所以以前,我一直看不懂二进制,对二进制也很排斥。总觉得没用,烧脑。但其实二进制的知识无论如何都是绕不开的知识点。想挽救更多饱受二进制或原码反码与补码困扰的小学生。我必须站起来做点什么。学妹叫我专家!什么是二进制数?在数字电路中,是指以2为底的数制,以2为底表示该制为二进制。在这个系统中,通常使用两个不同的符号0(代表零)和1(代表一)来表示[1]。在数字电子电路中,逻辑门的实现直接使用二进制,所以现代计算机和依赖计算机的设备都使用二进制。每个数称为一个位(Bit,Binarydigit的缩写)。其实二进制中的01对应的是数字电路中的开关,所以整个计算机中的一切都是二进制科学,而我们只需要研究一下数字类型的二进制,根据规律的原理,数字本身就是最直接的二进制。如果不说原码、反码、补码,只看二进制和十进制的关系,不考虑位数,我想大多数人都能理解。比如2的二进制:10,3的二进制:11,4的二进制:100,5的二进制:101,那么负数的二进制呢?-2二进制-10?-3二进制-11?后面跟着这么一个负数?另外,这种不确定长度的二进制如果是数组怎么在计算机内存中找到呢?用一张可能不太合适的图来展示一下:大家会说这样不好,所以在设计计算机数值类型之初,就明确表示计算机的基本数据是固定长度的,由两部分组成:一个符号位(一位)和一个值位(几位),其中符号位的0或1分别代表正数和负数,位数就是数据的大小。你可能会说:一个数字有多少位?数字太长,如果数据小(前面全为0),会造成浪费和内存浪费,数字太短,放不下。网友直接不好说。伟大的设计师当然考虑过这个问题。它们把数值二进制的长度分成不同的长度供你使用。Java中有这8种基本数据类型(1byte=8bit):比如你bytea=1,他在内存中是这样的:00000001如果你intb=1;因为int是32位的,那么在内存中是这样存储的:00000000000000000000000000000001你可能会问为什么不说负数呢?别着急,慢慢来,下面我们讲的是正码、反码、补码。另外需要注意的是,如果二进制加法溢出,溢出部分不会被记录,只保存有效部分,所以数据类型的选择也要考虑目标数据的大小范围。既然原码已经初步知道了二进制数的一些规律,就让它更暴力一些吧。原始代码是什么意思?原码为二进制的起始符号位,即最高位为符号位:该位为0表示正数,1表示负数(0有两种表示:+0和-0),其余位表示值的大小。它是否直接清楚地显示了一个值?原始代码的优点是它清楚地表达了一个值。能够清楚地知道二进制数代表什么,简单直观。但是我们能不能畅通无阻地使用原来的代码呢?当然不是,虽然原代码可以很方便的表示一个正数和一个负数,但是我们观察它的加法:正数的加法没有问题,但是负数的加法会失败。问题:负数相加只考虑绝对值的增加,没有考虑负数的特性。负数加上负数的绝对值是相反的,所以原来代码上负数的加法就成了一道难题,不行。反码负数的原码不能实现加法,因为原码如果进行加法,实现的是与符号无关的值的绝对值的加法。所以这就违背了负数的加法规则,计算机只能加法。我们只能从中算起。这时,一些大师发现了反码,反码的定义如下:正数的反码与原码相同;负数的反码是正数的逐位取反,符号位保持为1。因为负数原码的加法是相反的(即加一运算变成负一),我们认为,如果将负数原码中的数字01倒过来,那么这个数字会有更多有趣的东西。原来代码中本来比较大的数(-1、-2等)经过这样的转换后就显得很小了。原来很小的数字经过这样的转换后看起来很大。(也无法直观地看出这个数是什么)转换后的数相加(正数),01交换后即可进行正常相加的逻辑。添加负数似乎没问题。但是真的没问题吗?正负数可以通过反码畅通无阻?不不不。我们记得原代码中有+0,-0。但是不影响操作。看反码中+0和-0的情况:看,反码不行,怎么办?请看下面的补码分析。为什么在补码和反码中会出现这个问题呢?主要是正负0占了两个坑:就是如果用反码表示这个数,用它来做加法运算,在正数范围内玩是没问题的,而范围负数的里面玩还可以,但是从负数一步到正数的时候,会经过两个0(-0,+0)和两个零来重复。怎么表达呢?我们来看看这些数的反码规律:-3的反码:11111100-2的反码:11111101-1的反码:11111110-0的反码:11111111+的反码0:00000000,如果能把这些负数的反码加1,正负0的矛盾是不是很快就消失了?!!这就是所谓的补码:符号位不变,正数的补码是原码之和,反码一致,负数的补码是其反码加1。这样,我们就解决了所有的问题,进行了计算。其实我们也用补码来表示计算机中的所有值。对于补码,你真的不能直接看出来是什么。负数可能理解起来有点抽象。我们应该如何理解补码呢?我是这样理解的:二进制数把数据分为正负两部分,分别代表两个区间:什么意思?这意味着您可以将负数视为部分,将正数视为部分。各部分的值也是一样的:不管是负数还是正数走出符号位,都是从0000000到1111111(以字节为例)分布。如果前面的符号位为1,则表示负数。负数从小到大有128个(-128~-1),如果为0则正数从小到大有128个(0~127)。这样是不是更容易理解了!测试中解释了这么多原因,我们来测试一下,用下面的代码验证上面的结果//WeChat公众号:bigsaipublicclassMain{publicstaticvoidmain(String[]args){inta=-1;//1111111111111111111111111111intb=1;//00000000000000000000000000000001System.out.println(Integer.toBinaryString(a));//输出-1的二进制System.out.println(Integer.toBinaryString(b0));/前面的会省略**127+1:*01111111*+00000001*=10000000=-128*/bytec=127;byted=(byte)(c+1);System.out.println(d);/**-1+1*11111111*+00000001*=100000000(理论上)=00000000(只有8个有效位)=0*/bytee=-1;bytef=(byte)(e+1);System.out.println(f);}}输出结果:这段代码意在整理如下。综上所述,你是不是对原码、反码、补码有了更透彻的理解?不管你懂不懂,反正问问她懂不懂她说的:总结一下:原代码可以直接显示值的大小。结构为符号位+数值部分。符号位为0表示正数,1表示负数。反码是一种过渡码,在补码或原码补码转换过程中实际需要用到。规则是正数的反码等于原码,负数的反码符号位不变,值位0变为1,1变为0。补码,中的值计算机是以补码的形式计算的,有效解决负数的加法问题,符号位也可以直接参与运算。而且原码、反码、补码的转换非常简单。好了,本文到此结束,希望有收获的小伙伴可以分享给需要的人。