文章目录

  • 浮点数的二进制表示
    • IEEE二进制浮点数的表示
      • 1.符号位
      • 2.有效数字
      • 3.有效数字的精度
    • 阶码
    • 规格化二进制浮点数
    • 新建IEEE表示
      • 实数编码
    • 单精度数转换为十进制
  • 浮点单元
    • FPU寄存器栈
    • FPU寄存器
    • 专用寄存器
      • 舍入
      • FPU控制字
    • 浮点数异常
    • 浮点数指令集
      • 1.初始化(FINIT)
      • 2.浮点数据类型
      • 3.加载浮点数值 FLD
      • FILD
      • 加载常数
      • 保存浮点数值(FST FSTP FIST)
    • 算术运算指令
      • FCHS和FABS
      • FADD FADDP FIADD
      • FSUB FSUBP FISUB
      • FMUL FMULP FIMUL
      • FDIV FDIVP FIDIV
    • 比较浮点数值
      • FCOM FCOMP FCOMPP
      • 条件码
      • P6处理器的改进
    • 读写浮点数值
    • 异常同步

浮点数的二进制表示

十进制浮点数有三个部分组成:符号,有效数字和阶码。比如,在-1.23154*105中,符号为负,有效数字为1.23154,阶码为5

IEEE二进制浮点数的表示

x86处理器使用的三种浮点数二进制存储格式都是由IEEE标准754-1985——二进制浮点运算一一所制定。下表列出了他们的特点

精度 范围
单精度 32位:1位符号位,8位阶码,23位为有效数字的小数部分。大致的规格化范围:2-126—2127 也被称为短实数
双精度 64位:1位符号位,11位阶码,52位为有效数字的小数部分。大致的规格化范围为:2-1022—21023 也被称为长实数
扩展双精度 80位:1位符号位,15位阶码,1位为整数部分,63位为有效数字的小数部分。大致的规格化范围:2-16382—216383 也被称为扩展实数

由于三种格式比较相似,因此本节将重点关注单精度格式。

1.符号位

如果符号位为1,则该数为负;如果符号位为0,则该数为正。

2.有效数字

浮点数的有效数字由小数点的左右的十进制数字构成。十进制的数123.154用加权位计数法可以表示为下面的累加和形式

123.154=(1x102)+(2x101)+(3x100)+(1x10-1)+(5x10-2)+(4x10-3)

小数点左边的数字的阶码都位正,右边的数字阶码都为负

小数点右边数字还有一种表达方式,即把他们列为分数之和,其中分母为2的幂,例如:

.1011=1/2+0/4+1/8+1/16=11/16

3.有效数字的精度

用有限位数表示的任何浮点数都无法表示完整的连续的实数。例如:假设一个简单的浮点数格式有5位有效数字,那么将无法表示范围在1.1111-10.000之间的二进制数。比如,二进制数1.11111就需要更精确的有效数字。将这个思想扩展到IEEE双精度格式,就会发现53位有效数字无法表示需要54位或更多二进制数值。

阶码

单精度数用8位无符号整数存放阶码,引入的偏差为127,因此必须在数的实际阶码上再加上127

规格化二进制浮点数

大多数二进制浮点数都以规格化格式存放,以便将有效数字的精度最大化。给定任意二进制浮点数,都可以进行规格化,方法是将小数点移位,直到小数点左边只有一个1。阶码表示的是二进制小数点向左或向右移动的位数。示例如下:

非规格化 规格化
1110.1 1.1101x23
000101 1.01x2-4
1010001 1.010001x2-6

反规格化数:规格化操作的逆操作是将二进制浮点数反规格化。移动二进制小数点,直到阶码为0。如果阶码为正数,则将小数点右移,如果阶码为负数,则将二进制小数点左移,并在需要的位置前填充导数0。

新建IEEE表示

实数编码

一旦符号位 阶码和有效数字字段完成格式化和编码后,生成一个完整的二进制IEEE段实数就很容易了。首先设置符号位,然后是阶码字段,最后是有效数字部分。例如:下面表示的是二进制1.101x20

  • 符号位:0
  • 阶码:01111111
  • 小数部分:10100000000000000000000

偏移码(01111111)是十进制数127的二进制形式。所有规格化有效数字在二进制小数点的左边都有个1,因此,不需要对这一位进行显示编码。

单精度数位编码示例

二进制数值 偏移阶码 符号 阶码 小数部分
-1.11 127 1 01111111 11000000000000000000000
1101.101 130 0 10000010 10110100000000000000000

IEEE规范包含了多钟实数和非数字编码

  • 正零和负零
  • 非规格化有限数
  • 规格化有限数
  • 正无穷和负无穷
  • 非数字
  • 不定数

规格化和非规格化: 规格化有限数是指所有非零有限值,这些数能被编码为零到无穷之间的规格化实数。尽管看上去全部有限非零浮点数都应被规格化,但若数值接近于零,则无法规格化,当阶码范围造成的限制使得FPU不能将二进制小数点移动到规格化位置时,就会发生这种情况。假设FPU计算结果为1.0101111x2-129,其阶码太小,无法用单精度数形式存放。此时产生一个下溢异常,数值则每次将二进制小数点左移一位逐步进行非规格化,直到阶码达到有效范围

正无穷和负无穷:正无穷表示最大正实数,负无穷表示最大负实数。无穷可以和其他数值比较。负无穷小于正无穷,负无穷小于任意有限实数。任一无穷都可以表示浮点溢出条件。运算结果不能格式化的原因是,结果的阶码太大而无法用有效阶码的位数来表示。

NaN:NaN是不表示任何有效实数的位模式

特定编码:在浮点运算中,常常会出现一些特定的数值编码

单精度数转换为十进制

IEEE单精度数转换为十进制时,建议步骤如下:

  1. 若MSB为1,该数为负,否则该数为正
  2. 其后8位为阶码。从中减去127,生成无偏差阶码,将无偏差阶码转换为十进制
  3. 其后23位表示有效数字。添加1. 后面紧跟有效数字位,尾随零可以忽略。用形成的有效数字,第一步得到的符号和第二步算出来的阶码,就构成了一个二进制浮点数
  4. 对第三步生成的二进制数进行非格式化(按照阶码的值移动二进制小数点,如果阶码为正,则右移,如果阶码为负,则左移)
  5. 利用权位计数法,从左到右,将二进制浮点数转换为2的幂之和,形成十进制数

示例IEEE(0 10000010 01011000000000000000000)转换为十进制:

  1. 该数为正数
  2. 无偏差阶码的二进制值为11 十进制为3
  3. 将符号 阶码和有效数字组合起来即得该二进制数为1.01011x23
  4. 非规格化二进制数为1010.11
  5. 则该数的十进制值为10.75

浮点单元

Inter8086处理器设计使之只能处理整数运算。这对于使用浮点运算的图形和计算密集型软件来说就变成了麻烦。尽管也可以纯粹地通过软件来模拟浮点运算,但这样会带来严重的性能损失

FPU寄存器栈

FPU不使用通用寄存器,反之,它有自己的一组寄存器,称为寄存器栈。数值从内存加载到寄存器栈,然后执行计算,再将堆栈数值保存到内存。FPU指令用后缀形式计算算术表达式,这和惠普计算器的方法大致相同。比如,现有一个中缀表达式:(5*6)+4,其后缀表达式为:5 6 *4 +

中缀表达式(A+B)*C要用括号来覆盖默认的优先规则,与之等效的后缀表达式则不需要括号:A B + C *

中缀转为后缀的例子

中缀 后缀 中缀 后缀
A+B AB+ (A+B)*(C+D) AB+CD+*
(A-B)/D AB-D/ ((A+B)/C)*(E-F) AB+C/EF-*

表达式堆栈:在计算后缀表达式的过程中,用堆栈来保存中间结果

FPU寄存器

FPU有8个独立的 可寻址的80位数据寄存器R0-R7,这些寄存器合称为寄存器栈。FPU状态字中名为TOP的一个3位字段给出了当前处于栈顶的寄存器编号。例如 当TOP=011时 表示栈顶为R3。在编写浮点指令时,这个位置也称为ST(0)。最后一个寄存器为ST(7)

如同想的一样,入栈操作将top-1,并把操作数复制到标识为ST(0)的寄存器中,如果在入栈之前,TOP等于0,那么TOP就回绕到寄存器R7。出栈操作把ST(0)的数据复制到操作数,再将TOP+1。如果在出栈之前TOP=7,则TOP就回绕到寄存器R0。如果加载到堆栈的数值覆盖了寄存器栈内的原有数据,就会产生一个浮点异常

尽管理解FPU如何利用一组有限数量的寄存器实现堆栈很有意思,但这里只需要关注ST(n),其中ST(0)总是表示栈顶。从这里开始,引用栈寄存器时将使用ST(0) ST(1),以此类推。指令操作数不能直接引用寄存器编号

寄存器中浮点数使用的是IEEE10字节扩展实数格式,也被称为临时实数。当FPU把算术运算结果存入内存时,它会把结果转换成如下格式之一:整数 长整数 单精度 双精度 或者压缩二进制编码的十进制数

专用寄存器

FPU有6个专用寄存器

  • 操作码寄存器:保存最后执行的非控制指令的操作码
  • 控制寄存器:执行运算时,控制精度以及FPU使用的舍入方法,还可以用这个寄存器来屏蔽单个浮点异常
  • 状态寄存器:包含栈顶指针 条件码和异常警告
  • 标识寄存器:指明FPU数据寄存器栈内每个寄存器的内容。其中每个寄存器都用两位来表示该寄存器包含的是一个有效数 零 特殊数值还是为空
  • 最后指令指针寄存器:保存指向最后执行的非控制指令的指针
  • 最后数据(操作数)指针寄存器:保存指向数据操作数的指针,如果存在那么该数被最后执行的指令所使用

舍入

FPU尝试从浮点运算中产生非常精确的运算结果,但是在很多情况下这是不可能的,因为目标操作数可能无法精确表示计算结果。FPU可以在四种舍入方法中进行选择

  1. 舍入到最接近的偶数
  2. 向负无穷舍入
  3. 向正无穷舍入
  4. 向0舍入

FPU控制字

FPU控制字用两位指明使用的舍入方法,这两位被称为RC字段。字段数值如下:

  • 00:舍入到最接近的偶数(默认)
  • 01:向负无穷舍入
  • 10:向正无穷舍入
  • 11:向0舍入

浮点数异常

每个程序都可能出错,而FPU就需要处理这些结果。因而,它要识别并检测6种类型的异常条件:无效操作 除零 非规格化操作数 数字上溢 数字下溢以及模糊精度。前三个在全部运算操作发生前进行检测,后三个在操作发生后进行检测。

每种异常都有对应的标志位和屏蔽位。当检测到浮点异常时,处理器将与之匹配的标志位置1。每个被处理器标志的异常都有两种可能的操作:

  • 如果相应的屏蔽位置1 那么处理器自动处理异常并继续执行程序
  • 如果相应的屏蔽位清0,那么处理器将调用软件异常处理程序

大多数程序普遍都可以接受处理器的屏蔽响应。如果应用程序需要特殊响应,那么可以使用自定义异常处理程序,一条指令能触发多个异常,因此处理器要持续保存自上一次异常清零后所发生的全部异常。完成一系列计算后,可以检测是否发生了异常。

浮点数指令集

FPU指令集有些复杂,因此本节尝试对齐功能进行概述,并用具体例子给出编译器通常会生成的代码。此外,本节还将看到如何通过改变舍入模式来控制FPU。指令集包括如下基本指令类型:

  • 数据传送
  • 基本算术运算
  • 比较
  • 超越函数
  • 常数加载
  • x87FPU控制
  • x87FPU和SIMD状态管理

浮点指令名用字母F开头,以区别CPU指令,指令助记符的第二个字母(通常为B或I)指明如何解释内存操作数:B表示BCD操作数,I表示二进制整数操作数。如果这两个字母都没有使用,则内存操作数被认为是实数。比如,FBLD操作对象为BCD数值,FILD操作对象为整数,而FLD操作对象为实数

操作数:浮点指令可以包含零操作数 单操作数和双操作数。如果是双操作数,那么其中一个必然为浮点寄存器。指令中没有立即操作数,但是某些预定义常数可以加载到堆栈。通用寄存器EAX EBX…不能作为操作数。

整数操作数从内存加载到FPU,并自动转换为浮点格式。同样,将浮点数保存到整数内存操作数时,该数值也会被自动截断或舍入为整数。

1.初始化(FINIT)

FINIT指令对FPU进行初始化。将FPU控制字设置为037Fh,即屏蔽了所有浮点异常,舍入模式设置为最近偶数,计算精度设置为64位。建议在程序开始时调用FINIT,这样就可以了解处理器的其实状态

2.浮点数据类型

MASM支持的浮点类型有:

  • QWORD 64位整数
  • TBYTE 80位整数
  • REAL4 32位IEEE短实数
  • REAL8 64位IEEE长实数
  • REAL10 80位IEEE扩展实数

3.加载浮点数值 FLD

FLD指令将浮点操作数复制到FPU堆栈栈顶(ST(0))。操作数可以是32位 64位 80位的内存操作数或另一个FPU寄存器。FLD支持的内存操作数类型与MOV指令一样

FILD

FILD指令将16位 32位或者64位有符号整数源操作数转换为双精度浮点数,并加载到ST(0)。源操作数符号保留。FILD支持的内存操作数类型和MOV一致

加载常数

下面的指令将特定常数加载到堆栈,这些指令没有操作数

  • FLD1指令将1.0压入寄存器堆栈
  • FLDL2T指令将log210压入寄存器堆栈
  • FLDL2E指令将log2e压入寄存器堆栈
  • FLDPI指令将π压入寄存器堆栈
  • FLDLG2指令将log102压入寄存器堆栈
  • FLDLN2指令将loge2压入寄存器堆栈
  • FLDZ(加载零)指令将0.0压入FPU堆栈

保存浮点数值(FST FSTP FIST)

FST指令将浮点操作数从FPU栈顶复制到内存。FST支持的内存操作数类型和FLD一致。操作数可以为32位 64位 80位内存操作数或另外一个FPU寄存器

FSTP(保存浮点值并将其出栈)指令将ST(0)的值复制到内存并将ST(0)弹出堆栈

FIST(保存整数)指令将ST(0)的值转换为有符号整数,并把结果保存到目标操作数。保存的值可以为字或者双字。FIST支持的内存操作数类型与FST一致

算术运算指令

下表列出了基本算术运算操作。所有算术运算指令支持的内存操作数类型与FLD(加载)和FST(保存)一致,因此操作数可以是间接操作数 变址操作数和基址变址操作数等等

指令 作用
FCHS 修改符号
FADD 源操作数与目的操作数相加
FSUB 从目的操作数中减去源操作数
FSUBR 从源操作数中减去目的操作数
FMUL 源操作数和目的操作数相乘
FDIV 目的操作数除以源操作数
FDIVR 源操作数除以目的操作数

FCHS和FABS

FCHS(修改符号)指令将ST(0)中的浮点值的符号取反。FABS(绝对值)指令清除ST(0)中数值的符号,以得到它的绝对值,这两条指令都没有操作数

FADD FADDP FIADD

FADD(加法),如果FADD没有操作数,则ST(0)与ST(1)相加,结果暂存在ST(1)。然后ST(0)弹出堆栈,把加法结果保留在栈顶。如果是寄存器操作数,从同样的栈开始,将ST(0)加到ST(1)。如果是内存操作数,FADD将操作数与ST(0)相加

FADDP(相加并出栈)指令先执行加法操作,再将ST(0)弹出堆栈

FIADD(整数加法)指令先将源操作数转换为扩展双精度浮点数,再与ST(0)相加

FSUB FSUBP FISUB

FUSB指令从目的操作数中减去源操作数,并把结果保存到目的操作数。目的操作数总是一个FPU寄存器,源操作数可以是FPU寄存器或内存操作数。该指令操作数类型和FADD指令一致。

FUSB的操作与FADD相似,只不过它进行的是减法而不是加法。比如,无参数FUSB实现ST(1)-ST(0),结果暂存与ST(1)。然后ST(0)弹出堆栈,将减法结果留在栈顶。若FSUB使用内存操作数,则从ST(0)中减去内存操作数,且不再弹出堆栈

FSUBP(相减并出栈)指令先执行减法,再将ST(0)弹出堆栈

FISUB(整数减法)指令先把源操作数转为扩展双精度浮点数,再从ST(0)中减去该操作数

FMUL FMULP FIMUL

FMUL指令将源操作数与目的操作数相乘,乘积保存在目的操作数中。目的操作数总是一个FPU寄存器,源操作数可以为寄存器或者内存操作数。除了执行的是乘法不是加法外,FMUL的操作与FADD相同。比如,无参数FMUL将ST(0)与ST(1)相乘,乘积暂存于ST(1),然后将ST(0)弹出堆栈,将乘积留在栈顶。

FMULP(相乘并出栈)指令先执行乘法,再将ST(0)弹出堆栈

FIMUL与FIADD相同,只是它执行的是乘法不是加法

FDIV FDIVP FIDIV

FDIV指令执行目的操作数除以源操作数,被除数保存在目的操作数中。目的操作数总是一个寄存器,源操作数可以为寄存器或者内存操作数。其语法与FADD和FSUB相同。

除了执行的是除法不是加法外,FDIV的操作和FADD相同。比如,无参数FDIV执行ST(1)除以ST(0)。然后ST(0)弹出堆栈,将被除数留在栈顶。使用内存操作数的FDIV将ST(0)除以内存操作数。

若操作数为零 则产生除零异常。若源操作数为正 负无穷 零 或者NaN,则使用一些特殊情况

FIDIV指令先将整数源操作数转换为扩展双精度浮点数,再执行与ST(0)的除法

比较浮点数值

浮点数不能使用CMP进行比较,因为CMP是通过整数减法来执行比较的。取而代之,必须使用FCOM指令,执行FCOM。执行FCOM指令后,还需要采取特殊步骤,然后再使用JCC跳转指令。由于所有的浮点数都为隐含的有符号数,因此FCOM执行的是有符号的比较。

FCOM FCOMP FCOMPP

FCOM(比较浮点数)指令将源操作数与ST(0)进行比较。源操作数可以为内存操作数或者FPU寄存器

FCOMP指令的操作数类型和执行的操作与FCOM指令相同,但是它要将ST(0)弹出堆栈

FCOMPP指令与FCOMP相同,但是它有两次出栈操作

条件码

FPU条件码标识有三个:C3 C2和C0,用以说明浮点数的比较结果。C3 C2和C0的功能分别与零标志位(ZF) 奇偶标志位(PF)和进位标志位(CF)相同。

在比较了两个数值并设置了FPU条件码之后,遇到的主要挑战就是怎样根据条件分支到相应标号。这包括两个步骤

  • 用FNSTSW指令把FPU状态字送入AX
  • 用SAHF指令把AH复制到EFLAGS寄存器

条件码送入EFLAGS之后,就可以根据ZF CF和PF进行条件跳转

P6处理器的改进

浮点数比较的运行时开销大于整数比较。考虑到这一点,InterP6系列引入了FCOMI指令。该指令比较浮点数值,并直接设置ZF PF CF

读写浮点数值

  • ReadFloat:从键盘读取一个浮点数,并将其压入浮点堆栈
  • WriteFloat:将ST(0)中的浮点数以阶码形式写到控制台窗口

异常同步

整数(CPU)和FPU是相互独立的单元,因此,在执行整数和系统指令的同时可以执行浮点指令。这个功能被称为并行性,当发生未屏蔽的浮点异常时,它可能是一个潜在的问题。反之,已屏蔽异常则不成问题,因为FPU总是可以完成当前操作并保存结果。

发生未屏蔽异常时,中断当前的浮点指令,FPU发异常事件信号。当下一条浮点指令或者FWAIT指令将要被执行时,FPU检查待处理的异常。如果发现有这样的异常,FPU就调用浮点异常处理程序

如果引发异常的浮点指令后面跟的是整数或系统指令,则指令不会检查待处理异常——它们会立即执行

汇编之浮点数处理(CrackMe003前置知识)相关推荐

  1. 【软件创新实验室2021年暑假集训】Java技术培训——Java前置知识学习

    [软件创新实验室2021年暑假集训]Java技术培训--Java前置知识学习 文章目录 [软件创新实验室2021年暑假集训]Java技术培训--Java前置知识学习 前言 一.了解计算机 1.计算机的 ...

  2. 从零开始入门pwn(一):pwn的介绍以及部分前置知识

    目录 一.前言 二.pwn的介绍 pwn的定义 pwn的题目模式 前置知识 一.Linux 二.C语言 三.汇编语言 四.计算机组成原理 三.总结 一.前言 本人凭着一腔热血想要入门pwn,但却被pw ...

  3. 【软件创新实验室2021年寒假集训】Java技术培训——Java前置知识学习

    系列文章目录 [软件创新实验室2021年寒假集训]汇总篇 20级Java培训 第一天:[软件创新实验室2021年寒假集训]Java技术培训--Java前置知识学习 第二天:Java基础(一) 第三天: ...

  4. 【转】学习汇编前你应该知道的知识

    转载地址:http://www.zxbc.cn/html/20070611/22772.html 1.汇编需要什么工具和程序,到哪里下载?     目前阶段,汇编程序仅需要两个程序就够了.masm.e ...

  5. 汇编学习(1)——基础知识

    汇编学习(1)--基础知识 ---谨以此系列文章记录我的汇编学习.  关于汇编 说起汇编语言,那自然不得不想到机器语言,在汇编语言尚未诞生之际,程序猿们只能非常苦逼的敲着0和1,还要记住一大堆复杂难记 ...

  6. 从零写一个编译器(二):语法分析之前置知识

    项目的完整代码在 C2j-Compiler 前言 在之前完成了词法分析之后,得到了Token流,那么接下来就是实现语法分析器来输入Token流得到抽象语法树 (Abstract Syntax Tree ...

  7. 一篇文章指明做JavaWeb项目需要的前置知识+完整项目初解读(萌新必看,十分友好)

    前言 过了web这个阶段了,项目也完成了的我想给各位后来者总结一下我整个项目从开始到结束的经验,当然,也不是一帆风顺,报错有时候折磨的要死,废话不多说,现在就开始吧. 本文一共分为两个部分:纯理论科普 ...

  8. 区块连教程(一):前置知识-linux补充

    区块连教程(一):前置知识-linux补充 区块链教程(二):基础概念介绍 区块链教程(三):Solidity编程基础 区块链教程(四):搭建私链.web3.js基础 区块链教程(五):合约编写实战实 ...

  9. 20211003:数字滤波器前置知识,sinc函数与Sa函数

    sinc函数和Sa函数对比解析 原理分析 sinc函数,又称辛格函数.表达式为: sinc(t)=sin(πt)/(πt)sinc(t) = sin(πt)/(πt) sinc(t)=sin(πt)/ ...

最新文章

  1. 高德APP启动耗时剖析与优化实践(iOS篇)
  2. linux swap 内存交换分区调整
  3. WIN7 X64 Setup Oracle Developer Suite 10g
  4. Apache Hadoop YARN – ResourceManager--转载
  5. Http请求Response Code含义
  6. 51 单片机 跑马灯2
  7. 分支定界算法 matlab,分支定界法----整数规划matlab
  8. 引领三消新模式 《梦幻花园》商业化分析
  9. MISC-BUUCTF-9题-九连环-佛系青年等
  10. Oracle命令(二):Oracle数据库几种启动和关闭方式
  11. 1090 Highest Price in Supply Chain(甲级)
  12. 华为发布基于第二代英特尔®至强®可扩展处理器家族的新一代服务器
  13. matlab中通过滤波器,[转载]matlab 滤波器(转)
  14. nfcwriter下载 ios_iOS、iPadOS、macOS开启公测
  15. 互联网人员电脑上必备的免费办公软件
  16. 控件测试之单行文本框测试
  17. package、folder和source folder的区别
  18. 山西师范计算机科学与技术能转什么专业,太原师范学院可以转专业吗 太原师范学院新生转专业政策...
  19. 等一场春,行走在心上
  20. 装修公司网站源码模板电脑、手机端、微信三合一Asp.net

热门文章

  1. TF之TFOD-API:基于tensorflow框架利用TFOD-API脚本文件将YoloV3训练好的.ckpt模型文件转换为推理时采用的.pb文件
  2. 8.图片组件和动画效果--从零起步实现基于Html5的WEB设计器Jquery插件(含源码)...
  3. [UE4]编程师外挂Visual Assist X
  4. Linux 静态库 动态库
  5. 板邓:wordpress建站不得不知的安全防护(二)
  6. ReactiveCocoa的使用方法
  7. 使用 core dump 查找程序遇到严重问题退出的原因
  8. WIndows via C/C++ 学习(12)权限上下文
  9. AB1601定时器timer0,timer1简介
  10. mysql物理优化_mysql物理优化器代价模型分析【原创】