FPU (2) 一元二次方程式

2024-01-16 20:18
文章标签 一元二次方程 fpu

本文主要是介绍FPU (2) 一元二次方程式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


Ch 23 FPU (2) 一元二次方程式

這一章裏小木偶將延續上一章的指令,寫一個計算一元二次方程式的程式,ROOT.ASM。


一元二次方程式之根

底下小木偶就利用以上所提的 8087 指令求得一元二次方程式之根。根據國中所學的,假如有一個一元二次方程式 ax2+bx+c=0,則此方程式 x 之解為

一元二次方程式根之公式

底下這個程式是求 x2-25=0 方程式之解,如果您要求其他方程式的解,必須修改原始程式之的 lr_a、lr_b、lr_c 三係數 (在 MASM 6.x 裡,c 是保留字,所以為了使 MASM 5.x 與 6.x 都能順利組譯,我在係數之前加上 lr,表示長實數之意)。這個程式也只能由 SYMDEB.EXE 觀看結果,小木偶將於稍後再撰寫直接顯示 ST 之十進位數值於螢幕上的副程式。

;***************************************
code segment
assume cs:code,ds:code
org 100h
;---------------------------------------
start: finit ;---st--;-st(1)-;-st(2)-;-st(3)-;06
fld lr_b ; b ;
fmul lr_b ; b2 ; ; ; ;08
fld lr_a ; a ; b2 ;
fmul lr_c ; ac ; b2 ; ; ;10
fimul const ; 4ac ; b2 ;
fsubp st(1),st ;D=bb-4ac ; ; ;12
shr const,1
fsqrt ; SQR(D); ; ; ;14
fld lr_b ; b ; SQR(D)
fchs ; -b ; SQR(D); ; ;16
fld st ; -b ; -b ; SQR(D)
fadd st,st(2) ;-b+SQ(D) -b ; SQR(D); ;18
fild const ; 2 ;-b+SQ(D) -b ; SQR(D)
fmul lr_a ; 2a ;-b+SQ(D) -b ; SQR(D);20
fdiv st(1),st ; 2a ; x1 ; -b ; SQR(D)
fxch st(1) ; x1 ; 2a ; -b ; SQR(D);22
fstp x1 ; 2a ; -b ; SQR(D)
fxch st(1) ; -b ; 2a ; SQR(D); ;24
fsubrp st(2),st ; 2a ;-b-SQ(D)
fdivp st(1),st ; x2 ; ; ; ;26
fstp x2
int 20h ;28
org 160h ;29
lr_a dq 1.00 ;30
lr_b dq 0.00 ;31
lr_c dq -25.0 ;32
x1 dq ? ;33 其中之一根
x2 dq ? ;34 其中之一根
const dw 4 ;35
;---------------------------------------
code ends
;***************************************
end start

先來看看執行結果。

h:/homepage/source>e:symdeb root.com [Enter]
Microsoft (R) Symbolic Debug Utility Version 4.00
Copyright (C) Microsoft Corp 1984, 1985. All rights reserved.

Processor is [80286]
-G [Enter]

Program terminated normally (0)
-DL 160 L5 [Enter]
2117:0160 00 00 00 00 00 00 F0 3F +0.1E+1
2117:0168 00 00 00 00 00 00 00 00 +0.0E+0
2117:0170 00 00 00 00 00 00 39 C0 -0.25E+2
2117:0178 00 00 00 00 00 00 14 40 +0.5E+1
2117:0180 00 00 00 00 00 00 14 C0 -0.5E+1
-Q [Enter]

為了能夠方便觀察係數與根,所以小木偶在程式第 29 行加入『org 160h』,使得所有長實數變數能夠由位址 160H 開始,前三個長實數依次是二次方程式的係數,第四、五個長實數是兩根。在 SYMDEB 裏,『E』是表示 10 的冪方的意思,例如,位址 160H 的長實數是 +0.1E+1 就是表示 +0.1 乘以 10 的一次方,也就是 1 的意思。

至於為何我選在位址 160H 開始存放變數呢?其實 160H 這個位址的獲得經過假設再精確求得。該位址的限制是不能覆蓋程式碼,因程式不大,小木偶先假設位址為 1C0H,組譯連結後由 SYMDEB 觀察得知最後一行『INT 20H』在位址 157H,所以我再修改為 160H。

ROOT.ASM 程式有一個缺陷,假如 b2-4ac < 0,其平方根會產生錯誤,這時應使用 FPU 的比較指令進而產生條件跳躍,使程式轉向。

使 FPU 能條件跳躍

所謂條件跳躍就是比較兩數的大小,如果某數較大,就執行一段程式,若較小則執行另一段程式,類似這樣的跳躍稱為條件跳躍。在 FPU 的指令集並沒有能使 FPU 跳躍的指令,事實上 FPU 也無法改變 CPU 暫存器之值 (還記得要改變程式執行位址必須改變 CS:IP 之值),所以要產生 FPU 條件跳躍必須用間接方法。其步驟有四個:

  1. 先用 FPU 的比較指令改變 FPU 的狀態字組暫存器。
  2. 再用 FPU 的指令 FSTSW 把狀態字組存入一個記憶體變數裏。
  3. 將此記憶體變數存入 AH 暫存器,再用 CPU 指令 SAHF 指令存入 CPU 的旗標暫存器裏。此步驟使旗標暫存器之值等於狀態字組。
  4. 再用 CPU 指令 JL/JG/JE/JA/JB 來跳躍至正確處執行。

狀態字組

FPU 有五類暫存器,前章已介紹過最常用的堆疊暫存器,這裡將介紹狀態字組(status word)。顧名思義,狀態字組是一個 16 位元的暫存器,表示 FPU 的狀態,所謂狀態是指 FPU 是不是在忙碌中、是否除以零、是否無效運算、現在堆疊頂端是那一個堆疊暫存器等等,在此處我們要注意的是四個狀態碼位元,C0、C1、C2、C3。這些位 元分布如下圖:

8087 狀態字組

比較指令:FCOM、FCOMP、FCOMPP、FICOM、FICOMP

標題所見的這些比較指令都是以堆疊頂為目的運算元,而來源運算元可以是記憶體變數或是其他的堆疊暫存器,例如

FCOM    ST,ST(1)
FCOM ST,x

上述第一例是比較 ST 和 ST(1) 之數值,第二例是比較 ST 和記憶體變數 x 之數值,而這記憶體變數的形態可以是短實數和長實數。因為 FCOM 等指令均以 ST 為目的運算元,所以 ST 是可以省略不寫的,例如上述兩例,可以寫成下面的樣子,

FCOM    ST(1)
FCOM x

結果是一樣的。假如 ST 的比較對象是 ST(1) 的話( 來源運算元是 ST(1) ),連 ST(1) 也可以省略。至於 FCOMP 和 FCOMPP 分別是比較後彈出一次和彈出兩次,這裡彈出的數會消失不見並沒有存入記憶體中,這點和 FSTP 不同。而 FICOM 和 FICOMP 是用來比較整數的,而這來源運算元整數形態可以是字組整數和短整數。

FPU 的比較指令會改變狀態字組的 C3 和 C0 位元,C3、C0 位元是在狀態字組的第八、14 位元,如上圖。比較後依 ST 和來源運算元的大小,C3 和 C0 設定方式如下表:

  比較結果      C3  C0
----------------------------
ST>來源運算元 0 0
ST<來源運算元 0 1
ST=來源運算元 1 0

FSTSW 指令

FSTSW (store status word) 這個指令的功用是用來把狀態字組取出並存入AX暫存器或 16 位元長的記憶體變數裏,其語法是

FSTSW   mem16
FSTSW AX

注意!FSTSW 之後所接的運算原可以是 AX 或 16 位元長的記憶體變數,前者只能用在 387 等級以上的 FPU;至於 16 位元長的記憶體變數則是 8087 就可以使用了。為什麼只能接 AX 而不可接其他 16 位元的暫存器呢?主要是為了下面 SAHF 指令能很方便的把 AH 暫存器內的資料移到旗標暫存器裏,否則 FPU 的運算元是不能使用 CPU 的暫存器。

SAHF 指令

SAHF 是 8088 指令集中的一個指令,並非 FPU 指令。這個指令是把 AH 暫存器中的值移到旗標暫存器的較低的 8 個位元,這 8 個位元只有第七、六、四、二、零位元有用,第五、三、一位元沒有使用,請參考附錄二8088 的旗標暫存器。

所以假如 AH 為 0100 0000B,則執行 SAHF 指令後,零旗標會被設為一,您可以預測看看下列程式片段將會有何結果?

        mov     ah,01000000b
sahf
jz zf_set
mov dx,offset mes1
jmp short print
zf_set: mov dx,offset mes2
print: mov ah,9
int 21h
mes2 db '零旗標已設定$'
mes1 db '零旗標未設定$'

這程式片段會顯示出『零旗標已設定』。所以事實上,跳躍指令其實是僅僅看旗標暫存器的設定值決定如何跳到那裡去執行,至於旗標暫存器如何設定,在跳躍指令執行時是不須理會的

FPU 的狀態暫存器裏的 C3 和 C0 位元,經 FSTSW 指令傳到 16 位元記憶體變數,再接著移到 AX 暫存器裏時,AH 的第 6、0 位元就是表示 C3 和 C0 位元,您可以對照上面 8087 狀態字組與 8088 旗標暫存器兩張圖,發現 C3 恰好對應 ZF,C0 恰好對應 CF。之後再由 SAHF 指令把 AH 移到旗標暫存器,所以 C3、C0 就移到零旗標與進位旗標了,只要檢查這兩個旗標就可以比較 ST 與來源運算元的大小,而決定跳躍方向。

修改 ROOT.ASM 程式

前面所撰寫的求一元二次方程式兩根的程式 ROOT.ASM 有缺陷,在這裡小木偶將他修改如果判別式小於零,程式能轉向執行。修改之後程式如下:

;***************************************
code segment
assume cs:code,ds:code
org 100h
;---------------------------------------
start: finit ;---st--;-st(1)-;-st(2)-;-st(3)-;06
fld lr_b ; b ;
fmul lr_b ; b^2 ; ; ; ;08
fld lr_a ; a ; b^2 ;
fmul lr_c ; ac ; b^2 ; ; ;10
fimul const ; 4ac ; b^2 ;
fsubp st(1),st ;D=bb-4ac ; ; ;12
shr const,1
fcom zero ;判別式是否小於 0 14
fstsw status ;結果存於 status 變數裏 15
fwait ;等待儲存完畢 16
mov ax,status ;比較結果移入 AX 17
sahf ;比較結果移入旗標暫存器 18
jb d_less_0 ;若小於則跳躍到 d_less_0 處 19
fsqrt ; SQR(D); ; ; ;20
fld lr_b ; b ; SQR(D)
fchs ; -b ; SQR(D); ; ;22
fld st ; -b ; -b ; SQR(D)
fadd st,st(2) ;-b+SQ(D) -b ; SQR(D); ;24
fild const ; 2 ;-b+SQ(D) -b ; SQR(D)
fmul lr_a ; 2a ;-b+SQ(D) -b ; SQR(D);26
fdiv st(1),st ; 2a ; x1 ; -b ; SQR(D)
fxch st(1) ; x1 ; 2a ; -b ; SQR(D);28
fstp x1 ; 2a ; -b ; SQR(D)
fxch st(1) ; -b ; 2a ; SQR(D); ;30
fsubrp st(2),st ; 2a ;-b-SQ(D)
fdivp st(1),st ; x2 ; ; ; ;32
fstp x2
int 20h ;34 結束程式
d_less_0: ;35 判別式小於零,無實數解
mov dx,offset message ;36 印出無實數解
mov ah,9
int 21h
int 20h ;39 結束程式

org 1e0h ;41
lr_a dq 1.00 ;42
lr_b dq 0.00 ;43
lr_c dq 25.0 ;44
x1 dq ? ;45 其中之一根
x2 dq ? ;46 其中之一根
zero dq 0 ;47
const dw 4 ;48
status dw ? ;49 狀態字組
message db '無實數解$'
;---------------------------------------
code ends
;***************************************
end start

將他存為 ROOT2.ASM,並組譯成 ROOT2.COM 檔,在 SYMDEB.EXE 裏執行並觀察結果。如果判別式為負值,則停止運算而印出『無實數解』後終止程式。

修改後的程式增加了幾行,第 14 到第 19 行,也就是在做開平方根運算前先檢查 ST 是否小於零,此時 ST 內的數值就是所謂的判別式 (b2-4ac)。值得注意的是第 16 行,小木偶加上了 FWAIT 指令,確保 FPU 已經把狀態字組存入 status 變數後,CPU 才將 status 之值存入 AX 裏。小木偶也試過如果把這行拿掉,似乎也能正確執行。


常數指令

FPU 裏有 7 個指令是用來把常用的常數推入堆疊頂稱為『常數指令』。這些常數都是內建在 FPU 裏的數值,都屬於暫時實數,其有效位數達 19 位數,當程式需要用到時可以很快的載入,節省時間與記憶體。這 7 個常數指令介紹如下:

FLD1

載入 1.0。( 把 1.0 推入堆疊頂 )

FLDZ

載入零。( 把 0.0 推入堆疊頂 )

FLDPI

載入圓周率,3.141592653589793239。( 把 π 推入堆疊頂 )

FLDL2T

載入 Log210。( 把 Log210 推入堆疊頂 )

FLDL2E

載入 Log2e,e 是自然對數的底數。( 把 Log2e 推入堆疊頂 )

FLDLG2

載入 Log102。( 把 Log102 推入堆疊頂 )

FLDLN2

載入 Loge2,事實上 Loge2 等於 Ln 2。( 把 Loge2 推入堆疊頂 )


註一:事實上有另一個指令可以直接比較 ST 和零,這個指令是 FTST (可以記成 TeST)。

FTST 指令

FTST 後不須接任何運算元,比較後狀態字組的設定方式和 FCOM 相同:

比較結果   C3  C0
----------------------------
ST>0 0 0
ST<0 0 1
ST=0 1 0

回到首頁, 到第二十二章, 到第二十四章

这篇关于FPU (2) 一元二次方程式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/613775

相关文章

(函数)求一元二次方程的根(C语言)

一、运行结果; 二、源代码; # define _CRT_SECURE_NO_WARNINGS# include <stdio.h># include <math.h>//声明函数;//判断条件等于0时;void zeor(double a, double b);//判断条件大于0时;void bigzeor(double p, double q);//判断条件小于0时;voi

C语言题目:一元二次方程

题目描述 解一元二次方程ax^2+bx+c=0的解。 输入格式 a,b,c的值。 输出格式 输出两个解,按照大小顺序输出,一个解时需要打印两次,不用考虑无解问题,保留两位小数 样例输入 1 5 -2 样例输出 0.37 -5.37 代码解析 首先,代码通过#include <stdio.h>引入了标准输入输出库,以便使用printf和scanf等输入输出函数。

哈工大C语言程序设计精髓-计算一元二次方程的根v2.0

题目内容: 根据下面给出的求根公式,计算并输出一元二次方程的两个实根,要求精确到小数点后4位。其中a,b,c的值由用户从键盘输入。如果用户输入的系数不满足求实根的要求,输出错误提示 "error!"。程序中所有的数据类型均为float。     程序运行结果示例1: Please enter the coefficients a,b,c: 1,2,1↙ x1=-1.0000, x

输出一元二次方程胡两个实根

#include <stdio.h>int main(){float a,b,c,delt,x1,x2; printf("输入方程的三个系数:");scanf("%f%f%f",&a,&b,&c);delt = b*b-4*a*c;x1 = (-b+sqrt(delt))/(2*a);x2 = (-b-sqrt(delt))/(2*a);printf("x1=%.2f x2

C语言经典例题(8) --- 进制A+B、网购、及格分数、最高分数、计算一元二次方程

文章目录 1.进制A+B2.网购3.及格分数4.最高分数5.计算一元二次方程 1.进制A+B 题目描述: 输入一个十六进制数a,和一个八进制数b,输出a+b的十进制结果(范围-231~231-1)。 输入描述: 一行,一个十六进制数a,和一个八进制数b,中间间隔一个空格。 输出描述: 一行,a+b的十进制结果。 输入: 0x12 05 输出

第17关 一元二次方程

立即学习:C语言编程入门100题-17-第17关 一元二次方程-WangTeacher的在线视频教程-CSDN程序员研修院 #include <stdio.h>#include <math.h>// 定义根的结构体typedef struct {double real;double imag;} Complex;int main() {double a = 0.0;double b = 0

1058:求一元二次方程

【题目描述】 利用公式 求一元二次方程ax²+bx+c=0的根,其中a不等于0。结果要求精确到小数点后5位。 【输入】 输入一行,包含三个浮点数a,b,c(它们之间以一个空格分开),分别表示方程ax²+bx+c=0的系数。 【输出】 输出一行,表示方程的解。 若两个实根相等,则输出形式为:“x1=x2=...”; 若两个实根不等,在满足根小者在前的原则,则输出形式为:“x1=.

js计算一元二次方程的根

1. //计算ax^2+bx+c=0方程的根。 2.程序 <script>     var a,b,c,disc,x1,x2,p,q;     a=1,b=2,c=-3;     disc=Math.pow(b,2)-4*a*c;     if(disc<0){         document.write('无实根');     }else{         p=-b/(2*a);

技术资料:STM32F746NGH7,STM32L471ZGT6 IC MCU+FPU

描述:STM32F7 32 位 MCU+FPU 基于高性能的 ARM®Cortex-M7 32 位 RISC 内核®,工作频率高达 216MHz。Cortex®-M7 内核具有单浮点单元(SFPU)精度,支持所有 ARM® 单精度数据处理指令与数据类型。同时执行全套 DSP 指令和存储保护单元(MPU),增强应用安全性。(图左) 技术参数 芯片:STM32F746NGH7(STM32F746)

C语言实验——一元二次方程Ⅰ (sdut oj)

C语言实验——一元二次方程Ⅰ Time Limit: 1000MS  Memory Limit: 65536KB Problem Description 解一元二次方程ax 2+bx+c=0的解。保证有解 Input a,b,c的值。 Output 两个根X 1和X 2,其中X 1>=X 2。 结果保留两位小