第二章:计算机中的数据
Part 1:基本概念
1、进制及其转换
-
二进制->八/十六进制
-
二进制<->十进制
2、机器数和真值
-
真值
正常人类使用的十进制数
-
机器数
最高位表示符号位(整数为0,负数为1),剩下的是真值绝对值的二进制数
3、四种码(原码、反码、补码、移码)1
原码:Sign Magnitude
反码:Ones’ Complement
补码:Two’s Complement
-
为什么需要反码和补码?
人脑可以一眼看出第一位是符号位,然后进行加减乘除运算,但是对于计算机来说,加减乘除是最基本的运算,加入符号位判断,会使得电路变得复杂,于是有人想到让符号位也参与运算中来
-
反码和补码的本质定义
反码(Ones’ Complement):注意为“ones”,其中文翻译为“1们的补集”。这个名称直观的定义了反码的的计算方式如果有有x>0,则有
(-x)反=[1······1]-[x]原
补码(Two’s Complement):"2的补集"同样直观的定义了补码的计算方法
4、数据的存储和边界对齐
-
数据的存储2
- 大端模式:正常人直观的存储模式
- 小端模式:正常人讨厌的存储模式(与大端模式相反)
-
边界对齐
C/C++数据类型要求边界对齐,这是由于现代存储器常用低位多体交叉存储结果来实现读取加速
Part 2:数据的表示和运算
1、整数
整数常用四种码表示,详见1.3
-
位移运算
- 算术位移:操作对象为有符号数,位移时符号位不变,当对[-x]补进行右移时,要添1而不是0
- 逻辑位移:操作对象为无符号数,位移添0即可
-
加减运算
-
原码:正常运算
-
补码:同原码,只不过丢弃溢出位
[A+B]补码=[A]补码+[B]补码
-
-
乘除运算
3.1 原码
-
乘法:直接使用竖式乘法计算即可
-
除法:直接使用竖式计算即可
3.2补码
- 乘法:采用booth算法
- 除法:加减交替法
-
2、浮点数:IEEE 7543
-
科学计数法
为了表示一个庞大的数字,使用定点数是一件很不明智的事情(十分浪费空间),所以出现了科学计数法,即将一个数表示为如下的形式
( ± ) a . b ∗ 1 0 c (\pm)a.b*10^c (±)a.b∗10c
在10进制下,a有9种取值,因为0可以表达到10c次中去,计算机中的float变量采用同样的思想,由于二进制数只有2个取值0,1则对于二进制的科学计数法来说a的取值只能是1,即float变量可以表示为
( ± ) 1. b ∗ 2 c (\pm)1.b*2^c (±)1.b∗2c我们只需要存储符号位,b和c即可
-
float的存储和运算
2.1 float的存储
float将4B,也就是32位划分成了3个部分来存储符号,b,c
- 符号:1 bit(0表示正,1表示负)
- 指数部分:8 bit(采用移码表示,即真值=无符号数(将这八位视为无符号数)-偏移值(127))
- 尾数部分:23 bit
在内存中的顺序为 符号位->指数部分->尾数部分
2.2 float的运算
在加减运算是先让两个数的阶码保持一致后,再进行运算
-
float的范围和精度
-
范围
-
指数范围
float的指数有8位,将其视为无符号数,则其范围是0~255,考虑到其偏移值,范围是[-127, +128],由于其全0和全1有特殊的用途,其真实的范围是[-126, +127]
-
尾数范围
尾数部分最大是取全1,即1.11······1(小数点后23个1),结合最大的指数2127可知
1.11 ⋯ 1 ∗ 2 127 = ( 2 − 2 − 23 ) ∗ 2 127 ≈ 3.4 E 38 1.11\cdots1*2^{127}=(2-2^{-23})*2^{127}\approx3.4E38 1.11⋯1∗2127=(2−2−23)∗2127≈3.4E38
所以float的范围大概是[-3.4E38, +3.4E38]
-
-
精度
Float在内存中只占4个字节,即只能表示232个数字,但是,float能表示的范围却远超这个数量级,这是因为float变量表示的数据不是均匀分布的(相比于int变量的每隔1存放一个数据)
-
-
float特殊值
-
尾数全0
- 指数全0:表示0
- 指数全1:表示无穷
-
尾数不全为0
- 指数全0:将尾数位前默认的1修改为0,以提高表示小数的精度
- 指数全1:Not a Number(NaN)
-
Part 3:硬件电路实现
1、基本运算部件
首先观察1位bit相加
Input A | Input B | Carry(进位) | Result |
---|---|---|---|
1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 |
0 | 1 | 0 | 1 |
0 | 0 | 0 | 0 |
可以发现,进位的输出和与门(AND)相同,结果的输出和异或门(XOR)相同,由此可以构造出1位加法电路
有了上一位的进位,变要重新考虑1bit相加
Input A | Input B | 上一位进位 | 本位进位 | 结果 |
---|---|---|---|---|
1 | 1 | 1 | 1 | 1 |
1 | 1 | 0 | 1 | 0 |
1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 0 | 1 |
0 | 1 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 1 |
0 | 0 | 1 | 0 | 1 |
0 | 0 | 0 | 0 | 0 |
由此可以构造出1位全加器
1.1 ALU4
Part end:参考文献和一些说明
这个部分参考知乎上这个问题的回答(醉卧沙场大佬的回答 ):https://www.zhihu.com/question/352057791/answer/876413629 ↩︎
术语“little endian(小端)”和“big endian(大端)”出自Jonathan Swift的《格列佛游 记》(Gulliver’s Trabels)一书,其中交战的两个派别无法就应该从哪一端(小端还是大端)打开一个半熟的鸡蛋达成一致。一下是Jonathan Swift在1726年关于大小端之争历史的描述:“…下面要告诉你的是,Lilliput和Blefuscu这两大强国在过去36个月里一直在苦战。战争开始是由于以下的原因:我们大家都认为,吃鸡蛋前,原始的方法是打破鸡蛋较大的一端,可是当今皇帝的祖父小时候吃鸡蛋,一次按古法打鸡蛋是碰巧将一个手指弄破了,因此他的父亲,当时的皇帝,就下了一道敕令,命令全体臣民吃鸡蛋时打破鸡蛋较小的一端,违令者重罚。老百姓们对这项命令极为反感。历史告诉我们,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。这些叛乱大多都是由Blefuscu的国王大臣们煽动起来的。叛乱平息后,流亡的人总是逃到那个帝国去寻救避难。据估计,先后几次有11000人情愿受死也不肯去打破鸡蛋较小的一端。关于这一争端,曾出版过几百本大部著作,不过大端派的书一直是受禁的,法律也规定该派的任何人不得做官。”在他那个时代,Swift是在讽刺英国(Lilliput)和法国(Blefuscu)之间的持续的冲突。Danny Cohen,一位网络协议的早期开创者,第一次使用这两个术语来指代字节顺序,后来这个术语被广泛接纳了 ↩︎
参考资料这篇文章(float的精度和取值范围)https://blog.youkuaiyun.com/albertsh/article/details/92385277 ↩︎
机器字长是计算机进行一次整数运算所能处理的二进制数的位数,因此所有和整数运算相关的数据通路均与机器字长相同(例如ALU和通用寄存器) ↩︎