飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 4207|回复: 9

浮点运算简介

[复制链接]
  • TA的每日心情
    开心
    2017-2-23 16:41
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2005-12-18 12:29:45 | 显示全部楼层 |阅读模式
    [转帖] 浮点运算简介

    对于习惯于C的灵活多变的数据类型和方便的计算那些人而言,了解底层的浮点运算似乎没有什么意义,现在Visual盛行的时代还有多少人关心那些所谓的底层呢?
    对了AfOs来说,浮点运算是编程中很重要的一部分,因为我们可能会面临一些稍微复杂的运算,如果你和我一样是Die-hard的asm拥护者,不想轻易用C来解决问题,你肯定能想像在asm下用整数运算求sin(2.3)的痛苦,实际上,微机早就为我们准备了解决之道:那就是浮点运算.但现在关于浮点运算的资料较少,相信很多人和我一样还不掌握这种强有力的技术,那好,我们一起来学习学习.
                         


                                    (一)浮点数
                                        (This Part mainly Froe Bill's Article)
    在这之前,先来看几个术语:
    FPU->Floating Point Unit,浮点运算部件
    BCD->Binary Coded Decimal 压缩的二十进制数,是用4个位来表示数字0~9,一个byte表示两个十进制数,比如01111001表示89
    科学计数法:这是科学的~~~~具体含义查查初中还是小学的数学课本 D

    浮点运算使用三种不同的数据:
            1)整数(Integer),又分为字,短整数(Short Integer)和长整数(Long Integer)
            2)实数(Real)分单精度(Single Real)和双精度(Double Real)
            3)压缩的二十进制数(BCD)
       
        下面是其位数(bits)和能表示的大致范围和


            Type            Length          Range
            -----------------------------------------------
            Word Integer    16 bit          -32768 to 32768
            Short Integer  32 bit          -2.14e9 to 2.14e9
            Long Integer    64 bit          -9.22e18 to 9.22e18
            Single Real    32 bit          1.18e-38 to 3.40e38
            Double Real    64 bit          2.23e-308 to 1.79e308
            extended Real  80 bit          3.37e-1932 to 1.18e4932
            Packed BCD      80 bit          -1e18 to 1e18

    双精度数和扩展精度数表示范围对一般应用来说已经足够大了!

    1)整数,以补码形式存储,正数的补码是其本身,负数补码是其绝对值的各位变反后加1,下面是实际存储的例子:
            0024            var1 dw 24
            FFFE            var2 dw -2
            000004D2        var3 dd 1234
            FFFFFF85        var4 dd -123
            0000000000002694var5 dq 9876
            FFFFFFFFFFFFFEBFvar6 dq -321

    2)BCD数
            在FPU中用80位表示正好是浮点堆寄存器的宽度,在其格式如下存储:
        Bit
            79___72_71________________________________________0
            符号            ---18个二十进制数--------
    看下面的例子:
            00000000000000012345        var1  dt    12345
            80000000000000000100        var2  dt    -100

    3)浮点数,这个复杂点,有三种格式

            单精度:_31_30________23_22___________0
                  符号    指数        有效数

            双精度:_63_62__________52_51__________________0
                  符号    指数            有效数

            扩展精度数:
                  _79_78____________64_63___________________0
                  符号    指数              有效数
    例子:
            C377999A                    var1  dd      -247.6
            40000000                    var2  dd      2.0
            486F4200                    var3  real4  2.45e+5
            4059100000000000            var4  dq      100.25
            3F543BF727136A40            var5  real8  0.00123

            C377999A                    var1  dd      -247.6
            40000000                    var2  dd      2.0
            486F4200                    var3  real4  2.45e+5
            4059100000000000            var4  dq      100.25
            3F543BF727136A40            var5  real8  0.001235
            400487F34D6A161E4F76        var6  real10  33.9876

    DD和real4都可以在asm中来定义单精度浮点数,4 bytes
    DQ和real8都可以在asm中来定义双精度浮点数,8 bytes
    DT和real10都可以在asm中来定义扩展精度浮点数,10 bytes
                            
                            (二)浮点部件

    FPU从功能上分为两个部分:控制单元和运算单元,控制单元主要面向CPU,而算数单元负责具体算数运算.
    FPU即浮点部件包括8个通用寄存器,5个错误指针寄存器和三个控制寄存器.

    1)8个通用寄存器每个80 bit,形成一个寄存器堆栈,所有的计算结果都保存在寄存器堆栈中,其中数据全部是80位的扩展精度格式,即使是BCD,整数,单精度和双精度等在装入寄存器的时候都要被FPU自动转化为80位的扩展精度格式,注意栈顶通常表示为ST(0),然后是ST(1)...ST(i),ST(i)是相对于栈顶而言的.

    和堆栈很相似,只不过宽度为80bit,映像如下:
                    _______________________
                  |        ST(0)          |      
                  |_______________________|
                  |        ST(1)          |
                  |_______________________|
                  |    ......            |
                  |    ......            |
                  |        ST(i)          |            
                  |_______________________|

    2)控制寄存器,FPU有三个控制寄存器:状态寄存器,控制寄存器和标记寄存器

    状态寄存器->SW
            _M_____D________10___9____8___7_________5_________________________0__
          |  B |  C3| TOP| C2 | C1 | C0 | ES |    | PE | UE | OE | ZE | DE | IE |
          |____|____|____|____|____|____|____|____|____|____|____|____|____|____|

    B:    浮点部件正忙
    C0-C3  指示浮点运算的结果,不同指令有不同含义
    TOP    指示栈顶,通常是0
    ES 以下任何位置位 (pe, ue, oe, ze, de, or ie) 则置位
    PE 精度故障  
    UE 数字太小无法表示溢出  
    OE 现有精度无法表示,数字太大溢出  
    ZE 除0错  
    DE 指示至少有一个操作数未规格化  
    IE 无效错误,指示堆栈上溢或下溢,无效操作数等


    控制寄存器:
            _15____________10___9____8___7_________5______________________0__
          |              |IC | RC | PC  |    | PM | UM | OM | ZM | DM | IM |
          |____|____|____|___|__|_|__|__|____|____|____|____|____|____|____|

    IC 无穷大控制,对486,已经无效
    RC 舍入控制
            00 = 朝最接近或者偶数舍入
            01 = 朝负无穷大方向舍入
            10 = 朝正无穷大方向舍入
            11 = 超0方向截断
    PC 精度控制
            00 = 单精度
            01 = 保留
            10 = 双精度
            11 = 扩展精度
    PM~IM 屏蔽状态寄存器低5位指示的错误.为1则屏蔽.


    标记寄存器:
            每2 bit表示一个对应堆栈寄存器的状态,具体含义如下:
          15________________________________________3_____0
          |Tag7 |...................................|tag1|
          |_____|___________________________________|____|
            
    含义:
            00 = 有效
            01 = 零
            10 = 无效或无穷大
            11 = 为空

                                  
    (三)浮点指令系统及MASM下浮点程序设计

    事实上最重要和比较难于找到资料在(一)和(二)部分中已经介绍,下面是为了完整性的考虑,如果你是第一次接触浮点指令,看看下面的摘要也无妨.另外本文未涉及到的一个方面是关于浮点处理异常的情况,因为涉及到保护模式和中断、任务切换以及SEH等较多内容,我相信介绍之后只会令人更加迷惑,况且我现在似乎也无法把这几个问题完全说清除,一般我们几乎不需要知道这些.让我们先来看主要内容.

    关于浮点程序设计是一个大的话题,我只是提纲挈领地简述Masm32V7(/V6)中的设计方法,因为486以上的CPU内建了浮点部件所以可以在程序里直接使用浮点指令.下面是一个小例子:

    __MASMSTD  equ    1
    .386p
    .model flat, stdcall
    option casemap :none  ; case sensitive
    include c:\hd\hd.h
    include c:\hd\mac.h

    ;;--------------
        .DATA
    num1    dq      12345
    num2    dq      98765
    res    dd      0
            .DATA?
    buf    db 200 dup(?)

    ;;-----------------------------------------
        .CODE
    __Start:
            finit                  ;初始化浮点部件
            fild    num1            ;装入num1
            fild    num2            ;装入num2
            fmul                    ;执行乘法
            fist    res            ;存储
            invoke    wsprintf,addr buf,CTEXT("the result is: %ld",res        
            invoke    StdOut,addr buf ;显示,注意是控制台显示,编译用/SUBSYSTEM:CONSOLE
            
        invoke    StdIn,addr buf,20
            invoke    ExitProcess,0  
    END    __Start

    具体你要怎样运用指令,那就得看你自己所要进行的操作和要执行的算法了.注意在fpu内部寄存器总是以扩展精度数来表示数值的,因此进行整数运算最后要用fist来存储,这样才能得到正确的结果,这些转换是由fpu自动完成的.

    浮点指令系统分为五类:数据传送类、算术运算类、超越函数类、比较类、环境及系统控制类.
    我并不想列出所有函数的参数以及用法,因为这会是劳动力的浪费.我打字用拼音的!)具体参考资料见文章最后,别的我就帮不上你了.  

    1)数据传送类,主要包括
    这类指令主要是从内存装入浮点寄存器堆数据,一般目的地址总是栈顶ST(0),用调试器你可以清除的看到这一点.注意带P结尾的操作,是在前面操作完成之后出栈,也就是原来ST(1)的内容现在成了ST(0)的内容,注意到这一点,你可以方便地设计出灵活多变的程序.
    装入:     
            FLD    Push real onto stack
            FILD    Convert two's complement integer to real and push
            FBLD    Convert BCD to real and push to stack
    存储:     
            FST    Store floating-point number from stack
            FSTP    Convert top of stack to integer
            FIST     
            FISTP    Convert top of stack to integer
            FBSTP    Store BCD to integer and pop stack
    交换:     
            FXCH    Exchange top two stack elements
    常数装载:     
            FLD1    装入常数1.0
            FLDZ    装入常数0.0
            FLDPI    装入常数pi (=3.1415926....精度足够,放心使用)
            FLDL2E    装入常数log(2)e
            FLDL2T    装入常数log(2)10
            FLDLG2    装入常数log(10)2
            FLDLN2    装入常数Log(e)2

    我逼并不想列出所有的浮点指令的详细格式,因为没有必要!很多资料都有这些指令格式的介绍,浮点指令均以F开头,LD表示Load,ILD表示整数的Load,BLD是二十进制数的Load,这样记起来就很容易了,很多指令功能都可以根据指令一眼看出来.


    2)算术运算类
    加法:     
            FADD/FADDP    Add/add and pop
            FIADD    Integer add
    减法:     
            FSUB/FSUBP    Subtract/subtract and pop
            FSUBR/FSUBRP    Subtract/subtract and pop with reversed operands
            FISUB    Integer subtract
            FISUBR    Integer subtract/subtract with reversed operands
    乘法:     
            FMUL/FMULP    Multiply/multiply and pop
            FIMUL    Integer multiply
    除法:     
            FDIV/FDIVP    Divide/divide and pop
            FIDIV    Integer divide
            FDIVR/FDIVRP    Divide/divide and pop with reversed operands
            FIDIVR    integer divide with reversed operands
    其他:     
            FABS    Calculate absolute value
            FCHS    Change sign
            FRNDINT    Round to integer
            FSQRT    Calculate square root
            FSCALE    Scale top of stack by power of 2
            FXTRACT    Separate exponent and mantissa
            FPREM    Calculate partial remainder
            FPREM1    Calculate partial remainder in IEEE format

    如果指令后面未带操作数,其默认的操作数为ST(0)和ST(1),关于带R后缀的指令是正常操作数的顺序变反,比如fsub执行的是x-y,fsubr执行的就是y-x.

    3)超越函数类
    三角函数     
            FSIN    Calculate sine
            FCOS    Calculate cosine
            FSINCOS    Calculate quick sine and cosine
            FPTAN    Calculate partial tangent
            FPATAN    Calculate partial arctangent
    Log类     
            FYL2X    Calculate y times log base 2 of x
            FYL2XP1    Calculate y times log base 2 of (x+1)
            F2XM1    Calculate (2^x)-1

    4)比较类
            FCOM    Compare
            FCOMP    Compare and pop
            FICOM    Integer compare
            FTST    Integer compare and pop
            FUCOM    Unordered compare
            FUCOMP    Unordered compare and pop
            FXAM    Set condition code bits for value at top of stack
            FSTSW    Store status word

    会根据结果设置,C0~C3,在上面并未就C0~C3进行具体介绍,C1是用来判断上溢或者下溢的,C0相当于EFLAGS里面的CF,作用也基本一致,C2相当于PF,C3相当于ZF,你可能会看到如下指令
            FSTSW  ax
            SAHF
            JZ      label
    为什么如此呢,因为用如上指令将状态字存入EFLAGS,C0正好置于CF位,C3正好置于ZF位.

    5)环境及系统控制类
            FLDCW    Load control word
            FSTCW    Store control word
            FSTSW    Store status word
            FLDENV    Load environment block
            FSTENV    Store environment block
            FSAVE    Save coprocessor state
            FRSTOR    Restore coprocessor state

            FINIT    Initialize coprocessor
            FCLEX    Clear exception flags
            FINCSTP    Increment stack pointer
            FDECSTP    Decrement stack pointer
            FFREE    Mark element as free
            FNOP    No operation
            FWAIT    Wait until floating-point instruction complete

    我实在不想罗嗦了,因为这些指令的格式以及用法在Masm32V7的help目录下面的fphelp.hlp文件中有详细的说明,当然也还有很多其他的指令格式列表,我之所以列出来是为了完整性.这里还有一个比较困难的问题就是浮点数的显示,windows没有现成的函数调用,wsprintf只能显示整数,但有好多库支持,比如LYB主页上的浮点开发包,当然等你搞熟了这些东西,也可以自己写.

    关于浮点程序的调试,建议使用Softice,因为Trw不支持浮点堆栈的显示,现在网上有一个fpu插件,可以部分解决问题,不过不够好用.一切看你自己的选择了.
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2005-12-31 14:21:52 | 显示全部楼层
    好的学习资料 
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2005-12-31 22:20:34 | 显示全部楼层
    不错    虽然现在还看不懂吧  但是还是先收藏了
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-1-1 21:26:40 | 显示全部楼层
    收藏一下..
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2018-1-6 14:28
  • 签到天数: 6 天

    [LV.2]偶尔看看I

    发表于 2006-1-2 02:57:59 | 显示全部楼层
    原帖由 深海游侠 于 2006-1-1 21:26 发表
    收藏一下..



    游侠兄弟是PYG的贵宾啊```呵呵```
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-1-18 17:27
  • 签到天数: 30 天

    [LV.5]常住居民I

    发表于 2006-1-4 09:14:12 | 显示全部楼层
    学习,精典文章
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-1-5 10:54:21 | 显示全部楼层
    这个东西好像深奥
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-1-8 00:40:37 | 显示全部楼层
    说明:
    st(i):代表浮点寄存器,所说的出栈、入栈操作都是对st(i)的影响
    src,dst,dest,op等都是指指令的操作数,src表示源操作数,dst/dest表示目的操作数
    mem8,mem16,mem32,mem64,mem80等表示是内存操作数,后面的数值表示该操作数的内存位数(8位为一字节)
    x <- y 表示将y的值放入x,例st(0) <- st(0) - st(1)表示将st(0)-st(1)的值放入浮点寄存器st(0)

    1.  数据传递和对常量的操作指令

    指令格式
    指令含义
    执行的操作

    FLD src
    装入实数到st(0)
    st(0) <- src (mem32/mem64/mem80)

    FILD src
    装入整数到st(0)
    st(0) <- src (mem16/mem32/mem64)

    FBLD src  
    装入BCD数到st(0)
    st(0) <- src (mem80)

      

    FLDZ
    将0.0装入st(0)
    st(0) <- 0.0

    FLD1
    将1.0装入st(0)
    st(0) <- 1.0

    FLDPI
    将pi装入st(0)
    st(0) <- ?(ie, pi)

    FLDL2T
    将log2(10)装入st(0)
    st(0) <- log2(10)

    FLDL2E
    将log2(e)装入st(0)
    st(0) <- log2(e)

    FLDLG2
    将log10(2)装入st(0)
    st(0) <- log10(2)

    FLDLN2
    将loge(2)装入st(0)
    st(0) <- loge(2)

      

    FST dest
    保存实数st(0)到dest
    dest <- st(0) (mem32/mem64)

    FSTP dest
       
    dest <- st(0) (mem32/mem64/mem80);然后再执行一次出栈操作

    FIST dest
    将st(0)以整数保存到dest
    dest <- st(0) (mem32/mem64)

    FISTP dest
       
    dest <- st(0) (mem16/mem32/mem64);然后再执行一次出栈操作

    FBST dest
    将st(0)以BCD保存到dest
    dest <- st(0) (mem80)

    FBSTP dest  
       
    dest<- st(0) (mem80);然后再执行一次出栈操作


    2.比较指令

    指令格式
    指令含义
    执行的操作

    FCOM
    实数比较
    将标志位设置为 st(0) - st(1) 的结果标志位

    FCOM op
    实数比较
    将标志位设置为 st(0) - op (mem32/mem64)的结果标志位

      

    FICOM op
    和整数比较
    将Flags值设置为st(0)-op 的结果op (mem16/mem32)

    FICOMP op
    和整数比较
    将st(0)和op比较 op(mem16/mem32)后;再执行一次出栈操作

      

    FTST  
    零检测  
    将st(0)和0.0比较

    FUCOM st(i)  
       
    比较st(0) 和st(i)                  [486]

    FUCOMP st(i)      
       
    比较st(0) 和st(i),并且执行一次出栈操作

    FUCOMPP st(i)     
       
    比较st(0) 和st(i),并且执行两次出栈操作

    FXAM   
       
    Examine: Eyeball st(0) (set condition codes)


    3.运算指令

    指令格式
    指令含义
    执行的操作

    加法

    FADD
    加实数
    st(0) <-st(0) + st(1)

    FADD src
       
    st(0) <-st(0) + src (mem32/mem64)

    FADD st(i),st
       
    st(i) <- st(i) + st(0)

    FADDP st(i),st  
       
    st(i) <- st(i) + st(0);然后执行一次出栈操作

    FIADD src   
    加上一个整数
    st(0) <-st(0) + src (mem16/mem32)

    减法

    FSUB
    减去一个实数
    st(0) <- st(0) - st(1)

    FSUB src
       
    st(0) <-st(0) - src (reg/mem)

    FSUB st(i),st
       
    st(i) <-st(i) - st(0)

    FSUBP st(i),st
       
    st(i) <-st(i) - st(0),然后执行一次出栈操作

    FSUBR st(i),st
    用一个实数来减
    st(0) <- st(i) - st(0)

    FSUBRP st(i),st
       
    st(0) <- st(i) - st(0),然后执行一次出栈操作

    FISUB src
    减去一个整数
    st(0) <- st(0) - src (mem16/mem32)

    FISUBR src
    用一个整数来减
    st(0) <- src - st(0) (mem16/mem32)

    乘法

    FMUL
    乘上一个实数
    st(0) <- st(0) * st(1)

    FMUL st(i)
       
    st(0) <- st(0) * st(i)

    FMUL st(i),st
       
    st(i) <- st(0) * st(i)

    FMULP st(i),st
       
    st(i) <- st(0) * st(i),然后执行一次出栈操作

    FIMUL src
    乘上一个整数
    st(0) <- st(0) * src (mem16/mem32)

    除法

    FDIV  
    除以一个实数
    st(0) <-st(0) /st(1)

    FDIV st(i)
       
    st(0) <- st(0) /t(i)

    FDIV st(i),st
       
    st(i) <-st(0) /st(i)

    FDIVP st(i),st
       
    st(i) <-st(0) /st(i),然后执行一次出栈操作

    FIDIV src  
    除以一个整数
    st(0) <- st(0) /src (mem16/mem32)

    FDIVR st(i),st
    用实数除
    st(0) <- st(i) /st(0)

    FDIVRP st(i),st
       
    FDIVRP st(i),st

    FIDIVR src  
    用整数除
    st(0) <- src /st(0) (mem16/mem32)

      

    FSQRT
    平方根
    st(0) <- sqrt st(0)

      

    FSCALE
    2的st(0)次方
    st(0) <- 2 ^ st(0)

    FXTRACT
    Extract exponent:
    st(0) <-exponent of st(0); and gets pushed

    st(0) <-significand of st(0)

      

    FPREM  
    取余数
    st(0) <-st(0) MOD st(1)

    FPREM1
    取余数(IEEE),同FPREM,但是使用IEEE标准[486]

      
       
       

    FRNDINT  
    取整(四舍五入)
    st(0) <- INT( st(0) ); depends on RC flag

      

    FABS
    求绝对值
    st(0) <- ABS( st(0) ); removes sign

    FCHS
    改变符号位(求负数)
    st(0) <-st(0)

      

    F2XM1
    计算(2 ^ x)-1
      st(0) <- (2 ^ st(0)) - 1

    FYL2X   
    计算Y * log2(X)
    st(0)为Y;st(1)为X;将st(0)和st(1)变为st(0) * log2( st(1) )的值

      

    FCOS
    余弦函数Cos
    st(0) <- COS( st(0) )

    FPTAN
    正切函数tan
    st(0) <- TAN( st(0) )

    FPATAN
    反正切函数arctan
    st(0) <- ATAN( st(0) )

    FSIN
    正弦函数sin
    st(0) <- SIN( st(0) )

    FSINCOS
    sincos函数
    st(0) <-SIN( st(0) ),并且压入st(1)

    st(0) <- COS( st(0) )

      
       
       

    FYL2XP1  
    计算Y * log2(X+1)
    st(0)为Y; st(1)为X; 将st(0)和st(1)变为st(0) * log2( st(1)+1 )的值

    处理器控制指令

    FINIT
    初始化FPU
       

    FSTSW AX
    保存状态字的值到AX
    AX<- MSW

    FSTSW dest
    保存状态字的值到dest
    dest<-MSW (mem16)

      
       
       

    FLDCW src
    从src装入FPU的控制字
    FPU CW <-src (mem16)

    FSTCW dest
    将FPU的控制字保存到dest
    dest<- FPU CW

      
       
       

    FCLEX  
    清除异常
       

      
       
       

    FSTENV dest
    保存环境到内存地址dest处 保存状态字、控制字、标志字和异常指针的值

    FLDENV src
    从内存地址src处装入保存的环境
       

    FSAVE dest
    保存FPU的状态到dest处 94字节
       

    FRSTOR src
    从src处装入由FSAVE保存的FPU状态
       

      
       
       

    FINCSTP
    增加FPU的栈指针值
    st(6) <-st(5); st(5) <-st(4),...,st(0) <-?

    FDECSTP
    减少FPU的栈指针值
    st(0) <-st(1); st(1) <-st(2),...,st(7) <-?

      
       
       

    FFREE st(i)
    标志寄存器st(i)未被使用
       

      
       
       

    FNOP  
    空操作,等同CPU的nop
    st(0) <-st(0)

    WAIT/FWAIT
    同步FPU与CPU:停止CPU的运行,直到FPU完成当前操作码

      

    FXCH
    交换指令,交换st(0)和st(1)的值
    st(0) <-st(1)

    st(1) <- st(0)
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2006-8-5 22:35:15 | 显示全部楼层
    收藏一下!!
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2018-5-6 16:27
  • 签到天数: 7 天

    [LV.3]偶尔看看II

    发表于 2006-8-13 18:26:51 | 显示全部楼层
    原帖由 busheler 于 2006-1-8 00:40 发表
    说明:
    st(i):代表浮点寄存器,所说的出栈、入栈操作都是对st(i)的影响
    src,dst,dest,op等都是指指令的操作数,src表示源操作数,dst/dest表示目的操作数
    mem8,mem16,mem32,mem64,mem80等表示是内存操作数 ...


    主题帖看不懂,但这些个实例咱喜欢。。。
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表