汇编语言学习笔记(十二)-浮点指令

  • 浮点数如何存储
  • 浮点寄存器
  • 浮点数指令
  • 浮点计算例子
  • 浮点高级运算
  • CMOV移动指令

浮点数如何存储

浮点数的运算完全不同于整数,从寄存器到指令,都有一套独特的处理流程,浮点单元也称作x87 FPU。

现在看浮点数的表示方式,我们所知道的,计算机使用二进制存储数据,所表示的数字都具有确定性,那是如何表示浮点这种具有近似效果的数据呢,答案是通过科学计数,科学计数由符号,尾数和指数表示,这三部分都是一个整数值,具体来看一下IEEE二进制浮点标准:

格式 说明
单精度32位:符号占1位,指数占8位,尾数中的小数部分占23位
双精度64位:符号占1位,指数占11位,尾数中的小数部分占52位
扩展精度80位:符号占1位,指数占16位,尾数中的小数部分占63位

以单精度为例,在内存中的储存格式如下(左边为高位):

	| 1位符号 | 8位指数 | 23位尾数 |

其中符号位1表示负数,0表示正数,这与整数形式的符号位意义相同; 科学计数法表示形式如 m * (b ^ e),m为尾数,b为基数,e是指数,再二进制中,基数毫无疑问是2,对单精度,指数为中间8位二进制表示的数字,其中的尾数是形如1.1101 小数点后面的整数值。

关于指数,由于需要表示正负两种数据,IEEE标准规定单精度指数以127为分割线,实际存储的数据是指数加127所得结果,127为高位为零,后7位为1所得,其他双精度也以此方式计算。

为了解释内存中浮点数的存储方式,举一个浮点数的例子说明:

float test = 123.456;int main()
{return 0;
}

例子再简单不过了,仅仅定义了一个全局的float类型,我们通过gcc -S test.c来生成汇编,看看123.456是如何存储的,打开反汇编后的文件,看到符号_test后定义的数字是 1123477881(这里gcc定义成了long类型,不过没有关系,因为都是四字节数字,具体的类型还得看如何使用)。可以使用计算器把十进制数字转化为二进制:0 10000101 11101101110100101111001,这里根据单精度的划分方式把32位划分成三部分,符号位为0,为正数,指数为 133,减去127得6,尾数加上1.,形式为1.11101101110100101111001,扩大2 ^ 23次方为111101101110100101111001,十进制16181625,后除以2 ^ (23 – 6) = 131072,结果为123.45600128173828125,与我们所定义的浮点数正好相符。

浮点寄存器

这里介绍了浮点数的二进制表示,前面说过浮点单元计算使用独立的寄存器,在寄存器那篇也稍有提及,这里详细说明一下浮点单元的寄存器设施。

FPU有 8 个独立寻址的80位寄存器,名称分别为r0, r1, …, r7,他们以堆栈形式组织在一起,统称为寄存器栈,编写浮点指令时栈顶也写为st(0),最后一个寄存器写作st(7)。

FPU另有3个16位的寄存器,分别为控制寄存器、状态寄存器、标记寄存器,现一一详细说明此三个寄存器的作用:

状态寄存器,为用户记录浮点计算过程中的状态,其中各位的含义如下:

0 —— 非法操作异常
1 —— 非规格化操作数异常
2 —— 除数为0异常
3 —— 溢出标志异常
4 —— 下溢标志异常
5 —— 精度异常标志
6 —— 堆栈错误
7 —— 错误汇总状态
8 —— 条件代码位0(c0)
9 —— 条件代码位1(c1)
10 —— 条件代码位2 (c2)
11-13 —— 堆栈顶指针
14 —— 条件代码位3(c3)
15 —— 繁忙标志

其中读取状态寄存器内容可使用 fstsw %ax

控制寄存器的位含义如下:

0 —— 非法操作异常掩码 
1 —— 非法格式化异常掩码 
2 —— 除数为0异常掩码 
3 —— 溢出异常掩码 
4 —— 下溢异常掩码 
5 —— 精度异常亚曼 
6-7 —— 保留 
8-9 —— 精度控制(00单精度,01未使用,10双精度,11扩展精度) 
10-11 —— 舍入控制(00舍入到最近,01向下舍入,10向上舍入,11向0舍入) 
12 —— 无穷大控制 
13–15 —— 保留

其中读取控制寄存器和设置控制寄存器的指令如下:

# 加载到内存
fstcw control
# 加载到控制器
fldcw control

最后的标志寄存器最为简单,分别0-15位分别标志r0-r7共8个寄存器,每个寄存器占2位,这两位的含义如下:

11 —— 合法扩展精度 
01 —— 零 
10 —— 特殊浮点 
11 —— 无内容

另外对浮点寄存器的一些控制指令如下:

# 初始化fpu,控制、状态设为默认值,但不改变fpu的数据
finit# 恢复保存环境
fldenv buffer
fstenv buffer#清空浮点异常
fnclex#fpu状态保存
fssave

fstenv 保存控制寄存器、状态寄存器、标记寄存器、FPU指令指针偏移量、FPU数据指针,FPU最后执行的操作码到内存中。

浮点数指令

接下来将要详细说明其计算过程,要计算数据首先得看如何从内存中加载数据到寄存器,同时把结果从寄存器取出到内存,除了加载内存中的浮点数据指令,另外还有一些常量的加载,现列举如下:

指令 说明
finit初始化控制和状态寄存器,不改变fpu数据寄存器
fstcw control将控制寄存器内容放到内存control处
fstsw status将状态寄存器内容放到内存status处
flds value加载内存中的单精浮点到fpu寄存器堆栈
fldl value加载内存中的双精浮点到fpu寄存器堆栈
fldt value加载内存中的扩展精度点到fpu寄存器堆栈
fld %st(i)将%st(i)寄存器数据压入fpu寄存器堆栈
fsts value单精度数据保存到value,不出栈
fstl value双精度数据保存到value,不出栈
fstt value扩展精度数据保存到value,不出栈
fstps value单精度数据保存到value,出栈
fstpl value双精度数据保存到value,出栈
fstpt value扩展精度数据保存到value,出栈
fxch %st(i)交换%st(0)和%st(i)
fld1把 +1.0 压入 FPU 堆栈中
fldl2t把 10 的对数(底数2)压入 FPU 堆栈中
fldl2e把 e 的对数(底数2)压入 FPU 堆栈中
fldpi把 pi 的值压入 FPU 堆栈中
fldlg2把 2 的对数(底数10)压入 FPU 堆栈中
fldln2把 2 的对数(底数e) 压入堆栈中
fldz把 +0.0 压入压入堆栈中

以上指令虽多,但是还是很有规律,前缀f表示fpu操作,ld加载,st保存设置,p后缀弹出堆栈,s、l、t后缀表示单精度,双精度,扩展精度,c后缀表 示控制寄存器,s后缀表示状态寄存器。当然这仅仅是对AT&T语法而言,对MASM语法没有s,l,t之分,需要使用type ptr来指明精度,即内存大小。

学会灵活的加载弹出数据堆栈后,接下来就要看一些基本的计算:

fadd    浮点加法
fdiv    浮点除法
fdivr   反向浮点除法
fmul    浮点乘法
fsub    浮点减法
fsubr   反向浮点减法

对于以上的每种指令,有几种指令格式,以fadd为例,列举如下:

# 内从中的32位或者64位值和%st(0)相加
fadd source# 把%st(x)和%st(0)相加,结果存入%st(0)
fadd %st(x), %st(0)# 把%st(0)和%st(x)相加,结果存入%st(x)
fadd %st(0), %st(x)# 把%st(0)和%st(x)相加,结果存入%st(x),弹出%st(0)
faddp %st(0), %st(x)# 把%st(0)和%st(1)相加,结果存入%st(1),弹出%st(0)
faddp# 把16位或32位整数与%st(0)相加,结果存入%st(0)
fiadd source

这仅仅是对AT&T语法而言,对MASM源操作数与目的操作数相反!另外,对AT&T,与内存相关指令可加s、l指定内存精度。其中反向加法和反向除法是计算过程中目的与源反向计算。

浮点计算例子

接下来举一个AT&T语法的例子,来计算表达式的值 ( 12.34 * 13 ) + 334.75 ) / 17.8 :

# ( 12.34 * 13 ) + 334.75 ) / 17.8
.section .datavalues: .float 12.34, 13, 334.75, 17.8result: .double 0.0outstring: .asciz "result is %f\n"
.section .text
.globl _main
_main:leal values, %ebxflds 12(%ebx)flds 8(%ebx)flds 4(%ebx)flds (%ebx)fmulpfaddpfdivp %st(0), %st(1)fstl resultleal result, %ebxpushl 4(%ebx)pushl (%ebx)pushl $outstringcall _printf
end:pushl $0call _exit

前四个flds加载所有的数据到寄存器堆栈,可以单步运行并是用gdb的print $st0打印堆栈寄存器的值,可以看到为什么是堆栈寄存器。需要说明的是由于printf的%f是double类型的输出,所以最后要把一个8字节浮点放 到栈中传递,最终结果为27.818541,可以看到与计算器计算的结果近似相等。

浮点高级运算

除了基本的浮点计算,x87还提供了一些诸如余弦运算等高级计算功能:

指令 说明
f2xm1计算2的乘方(次数为st0中的值,减去1
fabs计算st0中的绝对值
fchs改变st0中的值的符号
fcos计算st0中的值的余弦
fpatan计算st0中的值的部分反正切
fprem计算st0中的值除以st1的值的部分余数
fprem1计算st0中的值除以st1的值的IEEE部分余弦
fptan计算st0中的值的部分正切
frndint把st0中的值舍入到最近的整数
fscale计算st0乘以2的st1次方
fsin计算st0中的值的正弦
fsincos计算st0中的值的正弦和余弦
fsqrt计算st0中的值的平方根
fyl2x计算st1*log st0 以2为底
fyl2xp1计算st1*log (st0 + 1) 以2为底

下面来看一下浮点条件分支,浮点数的比较不像整数,可以容易的使用cmp指令比较,判断eflags的值,关于浮点数比较,fpu提供独立的比较机制和指令,现对这组比较指令进行说明:

指令 说明
fcom比较st0和st1寄存器的值
fcom %st(x)比较st0和stx寄存器的值
fcom source比较st0和32/64位内存值
fcomp比较st0和st1寄存器的值,并弹出堆栈
fcomp %st(x)比较st0和stx寄存器的值,并弹出堆栈
fcomp source比较st0和32/64位内存值,并弹出堆栈
fcompp比较st0和st1寄存器的值,并两次弹出堆栈
ftst比较st0和0.0

浮点数比较的结果放入状态寄存器的c0,c2,c3条件代码位中,其值如下:

结果 c3 c2 c0
st0 > source000
st0 < source001
st0 = source100

如此倘若直接判断c0,c2,c3的值比较繁琐,所以可以使用一些技巧,首先使用fstsw指令获得fpu状态寄存器的值并存入ax,再使用sahf指令把 ah寄存器中的值加载到eflags寄存器中,sahf指令把ah寄存器的第0、2、4、6、7分别传送至进位、奇偶、对准、零、符号位,不影响其他标 志,ah寄存器中这些位刚好包含fpu状态寄存器的条件代码值,所以通过fstsw和sahf指令组合,可以传送如下值:

把c0位传送到eflags的进位标志 
把c2位传送到eflags的奇偶校验标志 
把c3位传送到eflags的零标志

传送完毕后,可以用条件跳转使用不同的结果值,另外需要说明的是浮点数相等判断,因为浮点数本身存储结构决定了它仅仅是一个近似值,所以不能直接判断是否相 等,这样可能与自己预期的结果不同,应该判断两个浮点数之差是否在一个很小的误差范围内,来决定这两个浮点数是否相等。

根据上面的技巧,使用fstsw和fpu指令组合,可以方便的使用浮点判断结果,这对我们是一种便利,而intel的工程师又为我们设计了一个组合指令,fcomi指令执行浮点比较结果并把结果存放到eflags寄存器的进位,奇偶,和零标志。

指令 说明
fcomi比较st0和stx寄存器的值
fcomip比较st0和stx寄存器,并弹出堆栈
fucomi比较之前检查无序值
fucomip比较之前检查无序值,之后弹出堆栈

判断结束后eflags的标志设置如下:

结果 ZF PF CF
st0 > st(x)000
st0 < st(x)001
st0 = st(x)100

CMOV移动指令

最后介绍的是类似cmov的指令,根据判断结果决定是否需要移动数据,其AT&T格式为 fcmovxx source, destination,其中source是st(x)寄存器,destination是st(0)寄存器。

指令 说明
fcmovb如果st(0)小于st(x),则进行传送
fcmove如果st(0)等于st(x),则进行传送
fcmovbe如果st(0)小于或等于st(x),则进行传送
fcmovu如果st(0)无序,则进行传送
fcmovnb如果st(0)不小于st(x),则进行传送
fcmovne如果st(0)不等于st(x),则进行传送
fcmovnbe如果st(0)不小于或等于st(x),则进行传送
fcmovnu如果st(0)非无序,则进行传送

以上可以看出,无论从寄存器的操作,还是计算过程,都比整数运算要繁琐的多,而且看似很简单的一个表达式,转化成浮点汇编需要做很多工作,由于其复杂性,同 一个表达式可以有多种运算过程,当然其中的效率相差很大,这依赖于对浮点汇编的理解程度,好在有高级语言处理相关工作,编写浮点指令的情况比较少见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能简述

人工智能研究的方向之一&#xff0c;是以所谓 “专家系统” 为代表的&#xff0c;用大量 “如果-就” &#xff08;If - Then&#xff09; 规则定义的&#xff0c;自上而下的思路。   人工神经网络 &#xff08; Artifical Neural Network&#xff09;&#xff0c;标志着另外…

Mockito 的使用

转自&#xff1a;Mockito 中文文档 ( 2.0.26 beta ) 转自&#xff1a;手把手教你 Mockito 的使用 参数匹配器 Argument Matcher(参数匹配器) Mockito通过equals()方法&#xff0c;来对方法参数进行验证。但是有时候我们需要更加灵活的参数需求&#xff0c;比如&#xff0c;匹配…

以SYSTEM用户运行CMD

在SCCM 经常会以NT AUTHOR\SYSTEM帐户操作。 安以下步骤可以以SYSTEM帐户打开一个CMD窗口。 1. 从微软网站下载PSTool。 2. 以管理员运行CMD&#xff0c;进入到解压的PSTool目录。 3. 运行psexec -i -s cmd.exe 4. 在新打开的CMD中运行whoami。 注&#xff1a;这个指令可以让你…

matlab cell

如果p为一个数&#xff0c;那么h(1)p,是没有问题的。 如果p为一个向量&#xff0c;那么h(1,:)p是没有问题的。 如果p是一个矩阵的话&#xff0c;上面的两种赋值方法都是会有错误的。 那么要如何处理呢&#xff1f; 这时就用到了cell数据类型了。cell的每个单元都可以存储任何数…

jboss 不适用内置日志_适用于孩子,父母和祖父母的JBoss HornetQ –第1章

jboss 不适用内置日志现在与HornetQ合作已经快4年了&#xff0c;我认为现在该分享我到目前为止所学的部分知识了。 这篇文章的主要目的不是重写官方文档 &#xff0c;而是以简单的方式阐明我们在PaddyPower中最常用的概念。 什么是HornetQ HornetQ是JMS实现。 JMS是一种面向…

Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon

客户端负载均衡&#xff1a;Spring Cloud Ribbon 一、负载均衡概念 负载均衡在系统架构中是一个非常重要&#xff0c;并且是不得不去实施的内容。因为负载均衡对系统的高可用性、 网络压力的缓解和处理能力的扩容的重要手段之一。通常所说的负载均衡指的是服务端负载均衡&#…

matlab cell,fix,floor,round取整的几种方式

ceil函数的作用是朝正无穷方向取整&#xff0c;即将m/n的结果向正无穷方向取整&#xff0c;如m/n3.12&#xff0c;则ceil(m/n)的结果为4。 类似的函数有如下几个&#xff1a; fix&#xff1a;朝零方向取整&#xff0c;如fix(-1.3)-1;fix(1.3)1; floor&#xff1a;朝负无穷方…

孤儿进程与僵尸进程[总结]

1、前言 之前在看《unix环境高级编程》第八章进程时候&#xff0c;提到孤儿进程和僵尸进程&#xff0c;一直对这两个概念比较模糊。今天被人问到什么是孤儿进程和僵尸进程&#xff0c;会带来什么问题&#xff0c;怎么解决&#xff0c;我只停留在概念上面&#xff0c;没有深入&a…

使用CloudForms实现云运营幸福感的3个步骤

本周宣布&#xff0c; Cloud Suite管理层的最新功能 CloudForms 4.2可供所有人使用。 产品增加了1800多种改进&#xff0c;令许多令人兴奋的新奇事物令人兴奋。 CloudForms是Cloud Suite产品的Cloud Management平台&#xff0c;使您能够跨区域联合部署它以进行集中管理操作&am…

leetcode 599. 两个列表的最小索引总和(Minimum Index Sum of Two Lists)

目录 题目描述&#xff1a;示例 1:示例 2:解法&#xff1a;题目描述&#xff1a; 假设Andy和Doris想在晚餐时选择一家餐厅&#xff0c;并且他们都有一个表示最喜爱餐厅的列表&#xff0c;每个餐厅的名字用字符串表示。 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如…

FAR,FRR,EER

FRR与FARFRR&#xff08;False Rejection Rate&#xff09;和FAR&#xff08;False Acceptance Rate&#xff09;是用来评估指纹识别算法性能的两个主要参数。FRR和FAR有时被用来评价一个指纹识别系统的性能&#xff0c;其实这并不贴切。指纹识别系统的性能除了受指纹算法的影响…

通过一段汇编,加深对寄存器ESP和EBP的理解

一直对寄存器ESP和EBP的概念总是有些混淆&#xff0c;查看定义ESP是栈顶指针&#xff0c;EBP是存取堆栈指针。还是不能很透彻理解。之后借于一段汇编代码&#xff0c;总算是对两者有个比较清晰的理解。下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码;假设执…

Redis和数据库 数据同步问题

Redis和数据库同步问题缓存充当数据库比如说Session这种访问非常频繁的数据&#xff0c;就适合采用这种方案&#xff1b;当然了&#xff0c;既然没有涉及到数据库&#xff0c;那么也就不会存在一致性问题&#xff1b;缓存充当数据库热点缓存读操作目前的读操作有个固定的套路&a…

matlab fspecial创建滤波算子

Fspecial函数用于创建预定义的滤波算子&#xff0c;其语法格式为&#xff1a;h fspecial(type) h fspecial(type,parameters,sigma)参数type制定算子类型&#xff0c;parameters指定相应的参数&#xff0c;具体格式为&#xff1a;typeaverage&#xff0c;为均值滤波&#xff…

hibernate jpa_JPA / Hibernate:基于版本的乐观并发控制

hibernate jpa本文是对Hibernate和JPA中基于版本的乐观并发控制的介绍。 这个概念已经很老了&#xff0c;上面已经写了很多东西&#xff0c;但是无论如何我都看到了它被重新发明&#xff0c;误解和滥用。 我在写它只是为了传播知识&#xff0c;并希望引起对并发控制和锁定的兴趣…

X86汇编快速入门

本文翻译自&#xff1a;http://www.cs.virginia.edu/~evans/cs216/guides/x86.html 本文描述基本的32位X86汇编语言的一个子集&#xff0c;其中涉及汇编语言的最核心部分&#xff0c;包括寄存器结构&#xff0c;数据表示&#xff0c;基本的操作指令&#xff08;包括数据传送指令…

Django(三)框架之第二篇

https://www.cnblogs.com/haiyan123/p/7717788.html 一、知识点回顾 1、MTV模型 model&#xff1a;模型&#xff0c;和数据库相关的 template&#xff1a;模板&#xff0c;存放html文件&#xff0c;模板语法&#xff08;目的是将变量如何巧妙的嵌入到HTML页面中&#xff09;。 …

使用GDB调试C库

用gdb调试程序时&#xff0c;一般的函数都可以step进去&#xff0c;可是C库函数却直接跳过了。 网上找了些资料&#xff0c;记录一下&#xff01; 1.安装C库的debug版本 [plain] view plaincopy print?sudo apt-get install libc6-dbg 安装完后&#xff0c;在/usr/lib目录下…

matlab imfilter对图像进行滤波

功能&#xff1a;对任意类型数组或多维图像进行滤波。 用法&#xff1a;B imfilter(A,H)    B imfilter(A,H,option1,option2,...)    或写作g imfilter(f, w, filtering_mode, boundary_options, size_options) 其中&#xff0c;f为输入图像&#xff0c;w为滤波掩模&…

MapStruct:将数据从一个bean传输到另一个bean

将数据从一种形式转换为另一种形式在IT行业中是一种被高度利用的概念。 MapStruct通过在编译时生成映射器实现&#xff0c;允许基于注释的Bean转换。 这样可以确保在运行时没有性能开销。 什么是MapStruct&#xff1f; MapStruct是一个代码生成器&#xff0c;它基于约定优于配…