128的补码
-128的补码2010-10-24 10:29-128的补码2010年10月23日星期六10:538位定点整数的原码表示范围是-127到127 8位定点整数的补码表示范围是-128到127
对上两句说明:
用原码表示:
01111111 8位有符号最大的数是127 11111111 8位有符号最小的数是-127
用补码表示:
01111111 8位有符号最大的数是127 10000001 8位有符号最小的数是-127
但是还有一个更小的就是10000000把这个规定为-128的补码
在8位长度下,-128的原码与反码都不存在,因为一个字节的有符号数的原码范围是:-127~+127,既然不存在-128的原码那么就无法求出-128的补码了,怎么办?
前几天听汇编老师曲俊华讲过,-128这样的数很特殊,不是取反加一求补码。
-2^n是个特殊数(n为x数值位的长度):它补码的求法应按照公式进行运算:
如:
-128
"[-2^7]补"=(2^8)+(-2^7)=10000000
原来还有更牛x的解释:一般的说法是负数的补码为其原码除符号位外取反然后总体加一,也就是说,要得到一个负数数的补码,要先知道这个负数的原码才行。那么,问题出现了,在8位长度下,-128的原码与反码都不存在,
127~+127,既然不存在-128的原码因为一个字节的有符号数的原码范围是:-
那么就无法求出-128的补码了,怎么办?
其实,这个问题的实际意义是,既然说计算机内部的有符号整数都是补码,那么怎么才能有
效的实现这一设计呢?潜台词是:根据上面由原码推导出补码的理论,如果是正数,计算机得到其原码,也就是得到了其补码(正数补码等同于原码),如果是负数,先得到其原码,然后再取反加一就可以了。也就是说按这个思维设计编译器,或计算机电路,就可以了。但是如果出现开始说的-128的补码问题,这个设计就不能工作了。其实,在真正的设计中,这种获得负数补码的"取反加一"的方案根本没有实施过~
拿现代的计算机举例,输入设备只有键盘,通过编辑器程序把键盘的扫描码变成ascii码存储起来,比如输入'a'就存储0x61,输入'5'就存储0x35,这些都是ascii码。当你想得到真正的机器数时(注意,机器数和ascii是不同的,字符'5'的ascii码是0x35,而数字5的机器数是0x5),需要借助编译器把表
就示数字的字符串从ascii码变成真正的机器数,比如你想得到56的机器数(是0x38),就可以输入语句"db 56",让汇编器程序帮你把56的ascii码字符串:"0x35,0x36",转变成真正的机器数0x38。这一转化需要这样一段程序:先把'5'与'0'做减法,就是0x35– 0x30得到0x5(这一步就将ascii字符5变成了真正的机器数5),再把0x5与0xa(就是十进制10)相乘得到0x32(就是十进制50),然后再把'6'与'0'做减法,就是0x36– 0x30得到0x6(就是机器数6),最后把0x32与0x6相加,得到0x38,就是机器数56了。
如果想得到-56,就用语句"db-56",这里得到机器数56的步骤与上面一致,只是最后要把0xff与0x38相乘,就是-1*56,最后得到0xc8,就是-56的补码。
这里我们看到,得到-56得补码时根本没用什么"取反加一",这里处理的过程都是很自然的,只要考虑各个数值的运算,而不用考虑数值的补码形式,以及如何得到负数的补码。为什么?因为加法,有符号乘法等指令的电路,都是按补码输入,补码输出来设计的,你只要保证输入的是补码,输出的肯定也是
补码。所以,只要你输入-1的补码0xff,与56的补码0x38,得到的自然是-56的补码0x c8。综上,我们在获得-56的补码时,没有采取先得到-56的原码,然后除符号位外各位取反,最后再总体加一的方式。
由此可见,计算机是一个相当"封闭"的系统,他内部所有的有符号整数都是补码形式存在的,只要按数值的实际意义考虑问题,不用担心它的存储方式,比如想让-56与6相乘,你根本不用担心结果那个负数怎么变成补码,所有的运算电路都是按补码设计的。换句话说,"封闭"的计算机内部的有符号数都是补码的形式存在的,你根本不用考虑什么原码,什么取反加一了,你只要考虑你想要的数值就可以了,不要担心他怎么存储的。
有了上面那个ascii码到机器数的转换程序后,数字的输入就不再是问题了,但是,考虑的更远一点,如果在计算机的"蛮荒时代",所有的指令,数据,都要用机器语言一位一位的输入时(搬开关、打孔),那时的有符号整数又怎么输入呢?确实,在那个环境下,就只能用我们的大脑计算了,把数字在大脑中转换成补码的样子,然后输入。其实,真正需要我们在大脑里转换然后再输入的数只有5个,分别是'+','-','0',0,-1,他们的补码分别是:0x2b,0x2d,0x30,0x00,0xff,好了,用这5个补码和机器指令编出我们刚才讲的ascii码转化机器数的程序,从此以后,我们只要输入数字的ascii字符串就可以了,让这个程序帮我们转换成补码,不用再辛苦的计算补码了。
深入到机器层,机器层面也根本用不着"原码取反加一",因为机器里的所有数字,都是人工或者是编译器输入的,已经转换成了补码了,他本身已经是一个完备而封闭的系统了,根本没有接口接收其他有符整数的编码方案了。需要注意的是有一个取补指令neg,这里的取补不是本来意义的"取反加一"(本来意义的"取反加一"只对负数),而是不论正负,把每一位取反,最后加一,就是说neg 20结果为-20,neg-56结果为56,就相当于把操作数与-1像乘了。这显然与为得到负数的补码采取的"取反加一"截然不同。当两个数做减法时,比如:有符号整数60(0x3c)与有符号整数77(0x4d)相减,加法器有一段电路把减数77取反加一,但是,
请注意,这个电路跟neg一样,不管正负每位取反最后加一,就相当于用乘法指令imul把操作数与-1相乘了。这也跟求负数的补码采取的"取反加一"截然不同。
综上,无论在编译器层面还是机器层面,"负数的补码为其原码除符号位外取反然后总体加一"这个方法都没有用上,这只是教科书上提供的便于记忆的方法而已。
根据上面的说法,分析下c中具体的问题:c中int占4个字节,表示范围从– 2147483648到2147483647。
问题1:int-2147483648;编译后的值正确吗?
上面说的转换程序,这个编译的步骤为:计算2*10^9+2*10^8+…+4*10+8,因为int只能表示到2147483647(0x7fff ffff),再加1的话就溢出了,得到了:0x8000 0000,(就是-2147483648),然后再把这个结果与-1就是0xffff ffff相乘,得到的结果也溢出了,为:0x8000 0000,但是这恰好是– 2147483648的补码,所以,虽然编译中虽然出现了两次溢出,但得到的结果是正确的。
问题2:int x=-2147483648;那么– x是2147483648吗?
因为– 2147483648(0x8000 0000)与-1(0xfff ffff)相乘结果为:0x8000 0000就是– 2147483648,所以– x的结果还是– 2147483648而不是2147483648。所以,凡是结果可能超越表示范围的时候,一定要慎重,因为结果可能是你意料之外的。
其他资料
-1和-128由于没有原码,所以就不能用符号位不变,其他位取反的方法求他们的补码了,而只能用补码的公式去求解.
即:二进制位串X为8位,对于大于等于-128小于0的,其补码公式为:
[X]补=2^8-|X|
若X=-1
按二进制算法求-1的补码即:100000000-00000001=11111111
按十进制算法求-1的补码即:256-1=255,然后将结果255转换成二进制11111111即可
同理:-128的补码求法:
按二进制算法求-128的补码即:100000000-10000000=10000000
按十进制算法求-128的补码即:256-128=128,然后将结果128转换成二进制
负75的补码怎么求10000000即可.
-1有原码,怎么说没有呢
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论