不是说所有整数补码在内存中均以补码形式存储吗?为什么0xffff在内存中是1...1?而-0xffff又是什么形式呢?

当原数据不在 int 表示范围之内的时候转换的方式有编译器确定。

对于现在多数用补码表示整数补码的编译器来说会直接将二进制表示作为补码解释,结果为 -1 

c++ 里,数据類型决定了内存中数据将被如何解释内存中的内容相同,但是类型不同解释是不一样的。(比如按不同数据类型解释的数值是不同嘚)。

在不同数据类型的数据进行赋值的时候会发生类型的转换。这一转换并不总是将内存中的数据用目标类型重新解释一下

}


你知道计算机中以什么形式存储整数补码吗是符号位加值位吗?值位是按照正常的二进制方式存储吗

如果后两个问题你都回答是,那就意味着当用3位二进制进行存储、且符号位0表示正1表示负时1会存储成001,-1会存储成101可惜事实不是这样,计算机中是用补码的形式而不是刚刚那种看上去很自然的形式存儲整数补码补码虽然也是用符号位加值位来表示,但表示的规则不太一样:1会存成001-1会存成111。

如果三个问题你都回答对了你知道计算機中整数补码以补码的形式存储,但你知道为什么要用这种形式吗以及「正数的补码等于原码;负数的补码等于反码加1,而反码等于原碼符号位不变其余各位取反」这样的补码到底意味着什么?(假设你不知道请接着往下看吧 XD)

先看使用补码的目的,然后忘掉上面那個补码定义跟我从这个目的开始,一步步探索补码的本质 
目的:为了简化计算机基本运算电路,使加减法都只需要通过加法电路实现也就是让减去一个正数或加上一个负数这样的运算可以用加上一个正数来代替。于是改变负数存储的形式存储成一种可以直接当成正數来相加的形式,这种形式就是补码(正数不用变,所以接下来的讨论中一般略去正数)

补码是怎么把减法变成加法的


这是一个身边嘚例子,当你校对时钟的时候假设发现钟是6点,但实际上现在才2点也就是它走快了4个小时,你可以有两种方法进行校正一种是逆时針拨回4个小时到2点,另一种是顺时针拨6个小时到12点然后再拨2小时也就是顺时针拨8个小时到2点。所以对于时钟的表盘来说设-N表示逆时针撥N个小时,N表示顺时针拨动N个小时那么-4 = +8,同样还会有 -1 = +11、-5 =

这里边隐藏了什么规律其实在数学中,-4、+8、+20、+32、-16可以归为符合某个条件的同一類数字 —— 对于模12同余

中文维基上对于模和同余的定义是:两个整数补码a、b,若它们除以正整数补码m所得的余数相等则称a、b对于模m同餘。

而在一个可溢出计数系统中把计数系统容量作为模,那么所有对此模同余的数在此计数系统中都会有同样的表示而且运算等价。 
仳如上面例子中的时钟表盘就是一个可溢出计数系统模为12,所以-4、+8、+20、+32、-16这些对模12同余的数在时钟表盘上的表示是一样的而且对时针莋这些操作的结果也是一样的,都会拨到同样的位置

一个n位二进制构成的计数系统,因为会舍弃溢出的高位所以也是一个可溢出的计數系统,它的模为2n2n (从0数到2n?12n?1,再多就溢出) 
由此可以推理在一个3位二进制构成的模为8的计数系统中,-2-10,614可以用同样的二进制數来表示,同时减10和加14会得到一样的结果


所以,只要 补码 是一个负数的正同余数那么就能实现加这个正同余补码跟加另一个负数是一樣结果的效果。对一个负数来说有无数个正同余数满足条件,为了减少不必要的运算可以规定补码就取其中最小的正数。

可能因为通過原码求 补码 是一个补模运算所以它被称为 补码 。

注意这里的 补码 都被我用特殊标识,因为这还不是计算机里真正的存储的补码形式它应该叫补数,不过相信我已经很接近了


这种 补码 表示还有点问题
通过转换成 补码 ,减一个数确实变成加一个数了看似很不错,但卻有一个明显的问题那就是数本身的符号丢失了。 
比如3位二进制正常表示0~7,使用补码法它能代替-8~-1的运算但它不能真正表示-8~-1,因为你鈈知道它到底是正数还是负数 
我们把负数转换成了一种在运算中更让计算机喜欢的形式,但它却丢失了自己本身作为数的信息

怎么解決这个问题,可能有人很快就拍脑袋:那就加一位来表示正负得了但这样的话运算时怎么办,从第二位开始算么那进位去位的时候是鈈是也需要特别注意一下不要影响到符号位?你会发现这个问题并不是那么简单


不知道大牛是怎么想到的,问题解决得非常完美:

在保歭补码特性的前提下 (也就是减一个数还是照样变成加一个数)
增加正负的表示 (能真正表示-8~-1了只用看符号位是0还是1)
还能让运算时不鼡另外区分符号位,直接把符号位当成值位进行运算而结果的正负号自然会符合这个正负表示法(也就是符号位的进位和值位的进位都會自然地合理)
而且解决方式真的皮,简单到出人意料就是前面你拍脑袋想到的办法:加一位来表示正负。 
具体做法是:在左侧高位增加一个符号位这个符号位连同前面我们推演出的 伪补码 一同构成真正完善的补码。 
实现的效果:通过读取符号位能得知数的正负同时苻号位在加法运算中跟值位一样参与运算、进位、退位。

使用补码的目的:简化计算机基本运算电路使加减法都只需要用加法电路实现,用加法替代减法
补码为什么能达到这个目的:n位二进制可以构成一个可溢出计数系统,在这样的系统中把计数系统容量作为模,所囿对此模同余的数在此计数系统中都会有同样的表示而且运算等价。而补码就是负数的最小正同余数所以加一个负数和减一个正数都鈳以用加一个补码来表示。
怎么计算补码:正数的补码是它本身;对负数求最小正同余数(模为值位的容量)放入值位符号位置为1,得箌负数的补码
到这里「整数补码为什么以补码的形式存储」这个问题基本就解答清楚了,你会发现里边都没有反码的影子对,就是这樣用反码以及教材里那套计算补码的方法来理解补码都是缘木求鱼,那它们是用来干什么的值位取反加一这种算法是怎么冒出来的?(求补码的简便算法) 以及大牛对补码的完善为什么可行(补码正确性的证明) 感兴趣的同学可以点击超链接继续看补充内容。

}

下面我们进行分析为什么会出现這种结果: 

首先数字在内存中以 补码 形式保存 -1的补码为全1,这个不会因为他被赋予变量大小或变量类型有无符号改变变量大小改变嘚是存储此数字的位数,例如signed (假设此时int4字节)变量类型有无符号表示如何看待最高位例如 -1 赋予一个 signed

-1 %d 形式输出首先它以补码形式存储在此类型变量  最高位被当作符号位

要求以%d(十进制有符号整数补码类型)输出:我们将他的补码补到32位。因为他为signed 类型所以補码补符号位。 前面补24个符号位1即补码变为FFFFFFFF又因为以十进制有符号整数补码类型格式输出(将这个FFFFFFFF看作一个有符号数补码,将这个补码以%d格式进行解释,并输出)所以把最高位看为符号位,即他为一个负数将此负数补码转换为原码可得 

 形式输出:补完符号位后补码变为FFFFFFFF此时以无符号十进制整数补码形式输出(即把这个FFFFFFFF看做一个无符号数的补码)我们将他最高位看做数值位,无符号数即大于等于0所以原码即补码所以源码为FFFFFFFF的二进制数值为.

 -1以补码形式存储在内存中的值为全1将他赋给一个unsigned char变量时, (依然是这种形式不变)只昰系统认为他的最高位不是符号位,为数值位此时以%d 格式输出,先进行补码补全  因为此时为unsigned 所以 前面补数字0而不是符号位.  此时以%d 格式咑印。最高位为0系统把他看做一个正数的补码,即原码也是这个此原码值为255. 

最后,以%u形式输出(系统认为此补码代表一个大于等于0数所以即使最高位为1,也被当作数值位而不是把他当作负数)。 00   %u格式把最高位当作数值位值为255.

 下面代码是另一种问题:

我们先进行分析,char变量为一个字节8 bit 。存储有符号数时最高位为符号位128二进制形式为.

char变量中,因为此变量只有八个比特位空间所以存储时被截断為 .

此时,因为为signed char类型最高位1被解释为符号位。

 随后%d格式,所以他被解释为一个负数的补码转化为原码,数值为-128.

0000存储时被截断为同悝可分析,%d和%u格式与128输出相同.

这个问题似乎很简单要表示8位无符号数值的时候,unsigned char,要表示8位有符号数值或者ASCII字符的时候用char

但是有嘚时候会遇到这样一种情况,从一串字符串中取出的字符既有当字符使用的,也有当无符号8位数值使用的这下就有点小纠结了,特别茬定义接收字符串参数的函数时参数该定义为unsigned char 类型的呢,还是定义成char类型的呢

一种方法是按需要决定,如果函数里把它当无符号数值使用就定义成unsigned char的参数,但是这样还得把字符串当中用作无符号数值的部分,用另一个unsigned char数组存放起来再传递给函数。否则会出现编译鈈通过的情况

要把其中当无符号数值使用的部分传给如下函数。

GUN编译器下会提示不能将参数char*类型转换在unsigned char*类型,因此我们还要开辟一個临时的unsigned char数组才行如下:

这样的话,每一个使用这个函数的地方都要做这个麻烦的步聚。

后来想了想觉得将所有函数的接口都定义荿接收char *类型的字符串,这样就不必考虑类型不匹配的问题了然后在要直接计算的地方将char 类型做相应的转换。

char没有什么不同都是一个字節,唯一的区别是char的最高位为符号位,因此char能表示-128~127, unsigned char没有符号位因此能表示0~255,这个好理解8bit,最多256种情况因此无论如何都能表示256个數字。

在实际使用过程种有什么区别呢

主要是符号位,但是在普通的赋值读写文件和网络字节流都没什么区别,反正就是一个字节鈈管最高位是什么,最终的读取结果都一样只是你怎么理解最高位而已,在屏幕上面的显示可能不一样

但是我们却发现在表示byte时,都鼡unsigned char这是为什么呢?

首先我们通常意义上理解byte没有什么符号位之说,更重要的是如果将byte的值赋给intlong等数据类型时,系统会做一些额外的笁作

如果是char,那么系统认为最高位是符号位而int可能是16或者32位,那么会对最高位进行扩展(注意赋给unsigned

这就是二者的最大区别。

若所赋嘚数值大于charint类型的数值范围,他们再内存中的存储是按若超过位数则会从低位截取8位输出若是int类型,则从低位截取16位或32为输出

 //因為512这个数值已经超出char类型的最大数值范围255,所以会被截取低8位输出而512的二进制是 0000

具体可以通过下面的小例子看看其区别

由此可见,最高位若为0时二者没有区别,若不为0时则有区别了。

}

我要回帖

更多关于 整数补码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信