首先,解释一下反码的概念:反码是数值存储的一种,简单点说就是在计算机内定点数的表示法之一,除了反码,常用的还有原码,补码,移码等。暗戳戳地说一下,反码其实是用得很少的~
但是!反码用处还是挺大的,比如说作为机器数运算的中间数,虽然用补码更多一些吧,再比如说系统环境设置,像linux平台的目录和文件的默认权限的设置umask就是的使用反码原理。
总之,存在即是合理,反码就是这么真实存在的一种编码方式。
其次,套用一下教科书上的东西:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
老陌在此用自己的理解解释一下,就是说求一个数的反码主要分成两个方向:正数和负数。其中最简单的就是正数了,只要原模原样照搬就可以了;然后是夹在正数和负数中间的0,因为在计算机中0的原码和反码是分成+0和-0的,所以这个特例还真要分类讨论;最后是负数,整数的做法也就是按位取反,符号位不变,小数的话印象中是有定点小数和浮点小数之分,我记得以前计算浮点小数还要先把小数转换成整数,然后算完了再把小数点向前移动多少位,最后才能求出实际的数值。
综上,在老陌的印象中求反码的难易等级是这样的:正整数(易)--->正小数--->零--->负整数--->负小数(难)
接下来老陌将以8位的二进制机器数为例,其中首位为符号位(0为正,1为负),所以决定真值范围的也是后面的7位。
当然,在老陌的印象中是学习过的双符号位的表示方法的,如果两个符号位不同就表示产生溢出。其中最高位(第一个符号位)表示正确的符号,01表示正溢出,10表示负溢出。相较于我们最开始学的单符号位,双符号位是可以参与运算的,所以更接近于我们小时候养成的数学运算的习惯。但是,这种表示方法(变形编码)一般是用在电子电路的设计里面的,所以在此老陌就不讨论了。
X=7,[7]真=[0000 0111]原=[0000 0111]反
X=-7,[-7]真=[1000 0111]原=[1111 1000]反
当然,还有一个特例:+0和-0。
X=0,[+0]真=[0000 0000]原=[1111 1111]反
X=0,[-0]真=[1000 0000]原=[0111 1111]反
接下来再来看一下小数的例子,其实用书上的概念来做还是挺简单的。
X=0.625,[+0.625]真=[0.10100000]原=[0.10100000]反
X=-0.625,[-0.625]真=[1.10100000]原=[1.01011111]反
p.s.不过在这里有一点需要特别注意一下,不是所有的小数都可以转化成二进制小数的,比如说0.73D=?B我相信算出来的二进制小数肯定是无限接近于十进制的0.73吧?
综上,求解一个机器数的反码其实挺简单的。
但是,忽然有这么一天,老哥发来了一张图片,然后“贼兮兮”地问我看得懂不?
老陌定睛一看:范围写得这么“装逼”?而且前面的公式里怎么会是加法?不会越界吗?
接下来老陌就带着疑惑去看了一下网上的资料,然后,我感觉老哥给我打开了一个新世界!有句话怎么说的来着,“计算机的世界有无数道门窗,无论我们打开哪一道,都能收获无穷尽的风景!”真的,这里边的风景很好,老陌忍不住想要记录下来!
首先纠正一点(其实看得懂的人也不需要纠正什么),上面的两个取值范围应该是这样的:
整数[x]反 = (2^ n+1 -1)+x 0>=x>-2^n * (mod (2^ n+1 -1)),
同理,小数[x]反 = (2 - 2^ -n)+x 0>=x>-1 * (mod (2 - 2^ -n+)),
这里想要强调的是最后面的取模并非只是对第一个被减数进行了操作,然后取模和前面的数字求出来的乘积才是取值范围中的临界点。
接下来再来解释一下为什么会是 (2^ n+1 -1) 和 (2 - 2^ -n) 以及最后为什么会是+x。
先来看负整数:
还是以X=-7为例,由于老哥给的公式是计算(n+1)位二进制数的,所以,这里的n=7,根据公式可得:
[-7]反 = [2^8-1+(-7)]
在这里(2^8-1)D 可以理解成1 0000 0000 B - 1 B ,也就是八位的最大二进制数 1111 1111
那么,这个公式是不是就可以这么解释:一个数的反码等于最大的那个数加上它本身?也就相当于是一个时钟里的指针转了一圈再次回到它本来的位置。
思及此处,老陌突然感觉这个理论和以前学过的同余定理的概念非常相似!简单来说就是:
许多的数被一个数d去除,有相同的余数。d数学上的称谓为模。如a=6,b=1,d=5,则我们说a和b是模d同余的。因为他们都有相同的余数1。
数学上的记法为:
a≡ b(mod d)
然后利用常见的结合律,交换律,传递律等等可以知道:
如果a≡x(mod d),b≡m(mod d),则
4)a+b≡x+m (mod d)
5)a-b≡x-m (mod d)
6)a*b≡x*m (mod d )
接下来让我们一起来验证一下:[-7]反= [1111 1111B] -[0000 0111B]=[1111 1000B] 结果和我们刚才算出来的反码是一样的!
接下来再来看一下负小数:
同样以X=-0.625为例,此时n=7,根据公式可得:
[-0.625]反=[2-2^(-7)+(-0.625)]
这里的 2-2^(-7) 可以这么提取公因式 2^(-7) *(2^8 - 1)
根据上面的例子我们可以知道(2^8-1)D 就是八位的最大二进制数 1111 1111 ,然后把它的小数点往前移七位 可以得到:1.111 1111 ,接着让我们一起来见证一个奇迹的时刻:
[-0.625]反=[1.111111-0.101B]=[1.010111B]
p.s.因为这里的小数点后面只有6位,所以为了规范我们需要自动补全末尾的两位,在原码里补的是0,反码对应的就是1,所以最后得出的答案是[-0.625]反=[1.0101 1111B]
在解题的过程中老陌还发现了一个“现象”:公式里的 +x 由于x是负数,所以后来都变成了 - |x| ,而实际运算过程中并不考虑前面第一个数字是否是符号位。这应该算是一个比较容易混淆的地方~
参考资料: