计算机中的数存放在寄存器中,一般将寄存器的位数称为机器字长, 你们说的无符号数其实就是不区分正负号的数,换句话说,就是没负数,全是正数,你们知道,计算机中的数是以0-1存储的, 假如咱们的寄存器16位, 无符号数表示的范围就是0~65535 (2^64=65536), 有符号就是分正负数,总数65536就被分红两半,一半正数,通常负数,范围就是 -32768~32767框架
有符号数,就是正负数同时存在, 人们当然能区分开整正负数,计算机怎么区分呢?学习
前面说了,计算机只认识01这样的数,因而人们规定 0表示正数, 1表示负数, 因而这样符号就被数字化了, 而且规定将其放在真实值前面, 因而有符号数就诞生了编码
如上图,按照计算机存数数据的特性将符号数字化, 数字化后的编码方式获得的结果称为机器数, 将带有+-符号的数字称为真值设计
既然如今将有符号数数字化后,新的问题来了,当这些机器数之间须要进行运算时, 符号位怎么办? 符号位可否参加机器数之间的运算呢? 若是说,须要参加运算又须要哪些处理才能消除符号位对计算结果的影响呢? 这一连串问题就引出了符号位和数值位所构成的编码: 原码 , 补码 , 反码 , 移码 3d
其实在学习的过程当中该一直问本身,本身在干什么??? 就好比如今,我在前面大概说了说计算机是如何表示数字的,因而认识了机器码,机器码之间须要进行运算于为了设计出使机器码运算的方式,人们对机器码进行不一样的变形编码,获得了,原码 , 补码, 反码 , 移码等, 下面看一下这几种编码的由来,以及他们对实现机器码的可计算的贡献code
原码是机器码最简单的一种变形,一样的它的符号位0表示正数,1表示负数。 数值位就是真值的绝对值blog
人们为了书写方便已经区分小数和整数,在符号位和数值位之间使用逗号分隔数学
举个例子:table
x= +1110, 那么它的原码就是 0,1110 x= -1110, 原码=1,1110
举个例子class
x= 0.1101 , 那么它的原码就是 0.1101 x= -0.1101, 原码=1 - (-0.1101) = 1.1101
看上面的原码计算方式,显然机器码很容易就转成原码,可是想一想若是用原码进行数值运算的话就会带来不少麻烦,咱们得先判断两个机器数绝对值的大小而后用大的减去小的,最终的符号再按照绝对值大的算, 并且咱们须要设计两套运算流程,一套给加法用,一套给减法用, 可是前辈们很智慧,由于人们找到了一种方式,找到了一个正数去替换原来减数位置的负数,相似像下面这样,实现了在计算机中仅仅设计一套加法器就实现加减法的运算
5-3=2 5+(-3)=2
上述方法的实现就依赖于下面的补码
补码的概念和补数的概念很像, 好比如今时钟六点了,咱们想让它指向三点,因而咱们能够往回转3(6-3=3)圈时针能回退到3点,也能够往前转9圈(6+9=15),能够前进到三点, 对时钟来讲往顺时针仍是逆时针的过程不同,可是对咱们来讲结果是同样的,都是三点了
这个过程就相似于,找到一个正数,让这个正数代替负数去参加运算,使用加法运算器也能获得正确的结果
时钟旋转一圈12小时,在这12小时中是不被显示且自动丢失的,也就是说 15-3=3 点, 因而咱们能够说,其实对时钟来讲, -3 +9 的做用实际上是一致的,结果都是三点, 在数学上咱们将12称为模 ,写成mod 12 , 咱们管9 称为是 -3以12为模的补数
因而咱们得知,只要咱们肯定了模,咱们就能求出这个数对这个模的地位相同的补数,或者说当咱们想将已知的负数转换成能够替换他的正数的话,借助模就能够完成
如何利用模求补数呢?
如何进行求模示例:
-3 全等于 +7 (mod10) +7 全等于 +7 (mod10) -3 全等于 +97 (mod100) -1011 全等于 +0101 (mod2^4) 2^4=1 0 0 0 0 - 1 0 1 1 --------------- 0 1 0 1 +0101 全等于 +0101 (mod2^4) 小数的mod = 2 +0.1001 全等于 +0.1001 (mod2) -0.1001 全等于 +1.0111 (mod2) 1 0.0 0 0 0 - 0.1 0 0 1 -------------- 1.0 1 1 1
求补码的公式
求负数补数的示例
其实你们能够看一下,对负数的公式来讲,公式中的n就是负数的位数, -1101 一共四位, n=4, 可是取的是n+1位, 换句话说是用一个比原负数多两位的数加上这个负数, 多出来一个符号位, 最后的结果中别忘了用 逗号分隔符号位和数值位, 固然这是为了方便咱们本身看,让人们一眼看去知道最开始的1是个符号位,后面的数才是 想求的补数结果
小数求补码的公式
举个例子: 求 -0.0110 的补码
此外, +0 -0的补码都是 0
从上面的讨论咱们知道,之因此想引入补码是为了消除减法运算,即将一个负数转换成它的正数补码,可是根据补码的定义,你们能够看到上面的两个例子,在产生补码的过程当中又出现了减法运算,怎么办呢?
因而咱们这样求补码: 先求原码, 而后变换这个原码获得补码, 怎么变换呢? 就是将除了 符号位的原码其它为取反 以后再加1
举个例子: 上面的就用 -1101 来讲, 以下:
因而看到这里咱们完全知道了,只为计算机设计一个加法器是彻底ok的,下文会介绍如何运算
经过上面的运算咱们知道下面的运算规则
原码(符号位,数值位) => 除符号位外其余位取反 = 反码 反码+1 = 补码 补码-1 = 反码
由此可知,其实这个反码就是原码和补码双方转换时的中间状态
真值转换成补码后,因为符号位和数值位是一块儿进行编码的,所以人们很难分清补码之间的大小
就像下面这样
十进制的21 对应二进制为+10101 补码为 0,10101 十进制的-21 对应二进制为-10101 补码为 1,01011 十进制的31 对应二进制为 +11111 补码为 0,11111 十进制的-31 对应二进制为-11111 补码为 1,00001
直观上看他们的大小是 101011>010101 100001>011111 而实际上偏偏相反
因而咱们这样, 在每个真值的基础上加上一个2^n , 状况就发生了变化
+10101 加上2^5 得 110101 -10101 加上2^5 得 001011 +11111 加上2^5 得 111111 -11111 加上2^5 得 000001
这样的话不须要借助补码,六位代码自己就能看到出真值的大小
更进一步,经过观察能够发现,其实一个数的补码和移码之间就差一个符号位,换句话说,若是咱们将补码的符号位从0换为1,或者从1换成0获得的就是它的移码, 在这基础上比较大小获得的结果是准确的
此外正负零的移码的同样的
计算机中的机器数的字长每每是固定的,当机器数左移n位或者是又移n位时,势必会却是另一边出现空位,那么在出现空位的位置究竟是补充1仍是填充0呢? 这取决于机器数是有符号仍是无符号,其中有符号的机器数采起的位移称为算数位移,无符号的惟一称为逻辑位移
真值 | 码制 | 补填代码 |
---|---|---|
正数 | 原码,补码,反码 | 0 |
负数 | 原码 | 0 |
负数 | 原码 | 左移添0 |
负数 | 补码 | 右移添1 |
负数 | 反码 | 1 |
不管是正数仍是负数,移位后的符号位都是不变的
举几个例子
机器数 十进制 移位前 : 0,0011010 +26 左移1位: 0,0110100 +52 右移1位: 0,0001101 +13
左移一位,除符号位外原来的最高位被移走了,右边空出的1位用0补全,可是高位丢失了其实获得的就是错误的结果,可是这个错误的结果刚好是原值的2倍,并且惟一运算速度还快,所以不少框架的底层都青睐使用这个位移运算的特性
每次右移时,最右边的数就会丢失,精度收到影响
左移一位至关于乘以2,右移1位至关于除以2
逻辑左移,高位丢失,低位填0, 逻辑右移,低位丢失,高位补0
回到一开始话题,计算机的运算方法,前面经过补码的介绍咱们知道了只设计一套加法器实际上是可行的,下面具体看一下是如何进行运算的
即 A-B = A + (-B)
补码的加法公式
整数: [A]补 + [B]补 = [A+B]补 (mod 2^n+1) 小数: [A]补 + [B]补 = [A+B]补 (mod 2)
对于减法来讲
整数: [A-B]补 = [A]+[-B]补 (mod 2^n+1) 小数: [A-B]补 = [A]+[-B]补 (mod 2)
最后看一个例子: 看看计算机如何将减法转换成加法并携带符号位运行得出正确结果
假设机器8位(含一位符号位),若A=+15 B=+24, 让咱们求 [A-B]补 ,并还原真值
A=+15 = +0001111 (算上+号一共八位) b=+24 = +0011000 (算上+号一共八位) A和B都是整数,因此他们的补码就是原码自己: [A]补 = 0,0001111 [B]补 = 0,0011000 [-B]原码 = 1,0011000 [-B]反码 = 1,1100111 ( 除符号位取反获得反码:) [-B]补 = 1,1101000 (由反码+1获得) [A-B]补 = [A]补 + [-B]补 = 0,0001111 + 1,1101000 = 1,1110111 那么 A-B = 啥呢? 反着换回去 1,1110111 1,1110110 (末位减1再取反) 1,0001001 = -0001001 = -9