16 | 浮点数和定点数(下):深入理解浮点数到底有什么用?
浮点数的二进制转化
十进制转为二进制的过程。例如:9.1
-
把整数部分转为二进制
-
把小数部分转为二进制
-
小数部分x2后大于1则二进制该位填1
-
-
乘上指数进退位,去除首位保留有效位数
-
按格式填入
- 符号位s+指数位e+有效位数f
浮点数的加法和精度损失
浮点数的加法:先对齐浮点数(小数有效位向右位移),在计算有效位
丢失精度: 在有效位进行右移的过程中,有效位中最右侧的数就被丢弃掉了。
两数相差的倍数超过有效位数则会被全丢弃
Kahan Summation算法
- 如何解决这个精度丢失问题呢?
- 解决:在每次的计算过程中,都用一次减法,把当前加法计算中损失的精度记录下来,在之后的计算中,先加上之前损失的精度再计算
示例代码
public class FloatPrecision {
public static void main(String[] args) {
float sum = 0.0f;
for (int i = 0; i < 20000000; i++) {
float x = 1.0f;
sum += x;
}
System.out.println("sum is " + sum);
}
}
对应的输出结果是:
sum is 1.6777216E7
public class KahanSummation {
public static void main(String[] args) {
float sum = 0.0f;
float c = 0.0f;
for (int i = 0; i < 20000000; i++) {
float x = 1.0f;
float y = x - c;
float t = sum + y;
c = (t-sum)-y;
sum = t;
}
System.out.println("sum is " + sum);
}
}
对应的输出结果就是:
sum is 2.0E7
总结延伸
一般情况下,在实践应用中,对于需要精确数值的,比如银行存款、电商交易,我们都会使用定点数或者整数类型。
浮点数呢,则更适合我们不需要有一个非常精确的计算结果的情况。
对于浮点数加法中可能存在的精度损失,特别是大量加法运算中累积产生的巨大精度损失,我们可以用Kahan Summation这样的软件层面的算法来解决。
17-建立数据通路(上):指令more运算=CPU
指令周期(Instruction Cycle)
- Fetch(取得指令),也就是从PC寄存器里找到对应的指令地址,根据指令地址从内存里把具体的指令, 加载到指令寄存器中,然后把PC寄存器自增,好在未来执行下一条指令。
- Decode(指令译码),也就是根据指令寄存器里面的指令,解析成要进行什么样的操作(指令在内存是以二进制数字存储),是R、I、J中的 哪一种指令,具体要操作哪些寄存器、数据或者内存地址。
- Execute(执行指令),也就是实际运行对应的R、I、J这些特定的指令,进行算术逻辑操作、数据传输或 者直接的地址跳转。
“Fetch - Decode - Execute”的循环,我们把这个循环称之为指令周期(Instruction Cycle)
建立数据通路
数据通路是处理器单元,由操作元件(ALU)和存储元件(寄存器)组成。通过数据总线的方式,把它们连接起来,就可以完成数据的存储、处理和传输了
ALU 的功能就是在特定的输入下,根据下面的组合电路的逻辑,生成特定的输出。
操作元件,也叫组合逻辑元件(Combinational Element)
存储元件,也有叫状态元件(State Element)
控制器
取指令,指令译码后发出控制信号交给ALU处理
CPU所需要的硬件电路
CPU运转需要的4种基本电路ALU这样的组合逻辑电路、用来存储数据的锁存器和D触发器电路、用来实现 PC寄存器的计数器电路,以及用来解码和寻址的译码器电路。
18-建立数据通路(中):指令more运算=CPU
- 组合逻辑电路:输出只跟输入有关,与电路原来的状态无关。
- 时序逻辑电路:输出除了跟输入有关,还跟电路原来的状态有关(以前的输入)
时序逻辑电路
时序逻辑电路用途
- 发出时钟信号
- 协调各个功能按照的时序
- 储存信息。时序电路实现的触发器有记忆功能
时钟信号的硬件实现
想要实现时序逻辑电路,第一步我们需要的就是一个时钟
这个按照固定的周期不断在0和1之间切换的信 号,就是我们的时钟信号(Clock Signal)。
反馈电路(Feedback Circuit):把电路的输出信号作为输入信号,再回到当前电路。
非门是输出结果接回输入的反相器(Inverter)
通过D触发器实现存储功能
利用这个开关和相同的反馈电路,我们就可以构造出一个有“记忆”功能的电路。
或非门真值只有(0.0)=1
这是个触发器(Flip-Flop),当两个开关都断开的时候,最终的输出结果,取决于之前动作的输出结果,这个也就是我们说的记忆功能。
复位置位触发器(Reset-Set Flip Flop) 。
在R-S触发器基础之上,加入了两个与门,同时给这两个与门加入了一个时钟信号CLK作为电路输入。可以实现控制往Q里写入数据的时间
一个与门可以“抑制”R-S触发器的一个输入端。只有CLK为1时才放出输入端的信号
- 当时钟信号CLK在低电平的时候,直接抑制两个输入端.RS都为0,结果为Q的输出不变
- 当时钟信号CLK在高电平的时候,释放R和S的信号,输出结果取决于R和S的开关。
D型触发器:用相反器把R和S连在一起,就可以通过一个开关同时控制R和S(结合CLK)来设置储存的值
一个D型触发器,只能控制1个比特的读写 ,N个并列就可以同时控制N位的读写
19-建立数据通路(下):指令more运算=CPU
PC寄存器所需要的计数器
时钟信号提供自增的频率,D型触发器可以储存数据。每次把储存的数据+“1”就是PC
PC自增实现:
加法器的两个输入,一个始终设置成1,另外一个来自于一个D型触发器A。我们把加法器的输出结果,写到 这个D型触发器A里面。于是,D型触发器里面的数据就会在固定的时钟信号为1的时候更新一次。
时序电路最核心的是时钟信号。 时钟信号控制加法计数、内存取值,乃至后面的命令执行的时序
单指令周期处理器(Single Cycle Processor):每一条指令,从程序计数,到获取指令、执行指令,都在一个时钟周期内完 成。
读写数据所需要的译码器
我们的数据能够存储在D型触发器里了。
- 问题:什么电路能实现寻址?
- 解决:2-1选择器
2-1选择器
- 实现:一个反相器、两个与门和一个或门
- 原理:一个反向器只能有0和1这样两个状态,所以我们只能从两个地址中选择一个。
- 本质:从输入的多个位的信号中,根据一定的开关和电路组合,选择出自己想要 的信号。
2开关数=可选择的地址数目。23=8
建立数据通路,构造一个最简单的CPU
D触发器(寄存器/时钟)、自动计数以及(地址/指令)译码器,再加上ALU,就凑齐了一个拼装一个CPU必须要的零件了。
- PC。有个会随着时钟主频不断地自增的自动计数器
- PC连着(地址)译码器,译码器根据PC的值对内存寻址
- CPU时钟控制读取出来的CPU指令写到指令寄存器(IR)中。
- 在IR后面接指令译码器(ID),把CPU指令解析成opcode和对应的操作数。
- 输出线路就要连接ALU,开始进行各种算术和逻辑运算。计算结果会写回到寄存器或内存中。
if…else分成两个指令实现,完全匹配 好了我们在电路层面,“译码-执行-更新寄存器“这样的步骤。
使用单指令周期处理器会根据慢速操作的时间来制定周期时间。所以会浪费大量的CPU时间(产生内部时间碎片)。因此,现代我们优化CPU的性能时,通过流水线、分支预测等技术,来实现在一个周期里同时执行多个指令。