ぐだぐだ低レベルプログラミング(224)x86(16bit)、指数関数の計算

Joseph Halfmoon

前回は「超越関数」のうち三角関数の計算例でした。まだ逆三角関数が残っているのだけれど見ないフリをして先に進めます。今回は指数関数です。フツーの関数電卓などでは x^y 的なボタンがあって一撃なのだけれど、8087においてはそうは問屋が卸してくれません。指数関数の底の変換公式が登場。忘れているよゐこは思い出さないと。

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3

※オリジナルの8086+8087の組み合わせでは8086と8087の同期をとるためにWAIT命令が必用になる場合があります。後継機種ではWAIT命令が不要(演習に使用しているエミュレータQEMUでも不要)なのでWAIT命令は使用しません。メンドイし。

F2XM1命令

指数関数を計算するのに、8087が用意してくれている命令はF2XM1という以下のような計算をする命令が一つだけです。

2^x – 1

2の「スタックトップの値」乗ひく1を計算してくれるもの。勿論、Fを頭にいただく命令である以上、入力オペランドとなる指数 x は浮動小数でOKです。ただし、オペランドの範囲の制限はキツイです。これを超えるとNaNが返る筈。

    • 8087、80287:オペランドは0≦ x ≦0.5
    • 80387:オペランドは-0.5≦ x ≦0.5
    • 以降のプロセッサ?:オペランドは-1.0≦ x ≦1.0

小数点以下のべき乗のところはメンドーみてやるから、小数点以上の部分はプログラマ様がよきに計らってね、という感じ。

そして、2のべき乗しか計算できないじゃん、ということではまったくありません。昔(高校?中学?)習った筈の、指数関数の底の変換公式を思い出せよ、ということらしいです。

\( y^x = a^{x log_a y} \)

そして8087は、以下の定数を内部に持っているのでアリガチな 10のべき乗や、e(自然対数の底、ネイピア数)のべき乗は変換公式で一撃であると。

    • FLDL2T命令=定数 log_2(10)
    • FLDL2E命令=定数 log_2(e)

なお、任意のYについてのlog_2を求める方法は次回の対数関数でやる予定です。それがあれば、yのx乗もバッチリ、ホントか?

今回実験のプログラム

さて今回実験のプログラムは、以下の「小さい」指数の指数関数値を各1点づつ計算するというものです。勿論上記の「指数関数の底の変換公式」を利用。

    • 10^0.1 = 1.2589254117941672104239541063958
    • e^0.1 = 1.1051709180756476248117078264902

なお、やたら長大な値が書かれているのは、Windows付属の関数電卓で計算した結果です。上記の数字に「似た」数字が返ってくれば、実験プログラムはまあ良しとするかと。

なお例によって強力なx86用アセンブラNASM用のソースです(MSのMASMともインテルASM86とも微妙に異なるけど、まあ分かるっしょ。)

%use fp

segment code
..start:
    mov ax, data
    mov ds, ax
    mov ax, stack
    mov ss, ax
    mov sp, stacktop
test:
; 10^[src1 = 0.1]
    mov bx, src1
    fld dword [bx]
    fldl2t
    fmulp
    f2xm1
    fld1
    faddp
; e^[src1 = 0.1]
    mov bx, src1
    fld dword [bx]
    fldl2e
    fmulp
    f2xm1
    fld1
    faddp
fin:
    mov ax, 0x4c00
    int 0x21
    resb    2048

segment data    align=16
    resb    1024 * 63
src1: dd 0.1

segment stack   class=STACK align=16
    resb    2048
stacktop:
    dw 1024 dup (0)
stackend:
アセンブルして実行

MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fexp.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)

なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます。

nasm -f obj -l fexp.lst fexp.asm
wlink name fexp.exe format dos file fexp.obj
debugx fexp.exe

まずはプログラムの逆アセンブルリスト。unasmFexpEC

10の0.1乗(0.1はスタックトップに格納)を求めた後の様子が以下に。expCase10_01

eの0.1乗(0.1はスタックトップに格納)を求めた後の様子が以下に。expCaseE_01

まあ、10のべき乗も、eのべき乗も求まることは分かった。次回は対数関数じゃね。

ぐだぐだ低レベルプログラミング(223)x86(16bit)、SIN、COS、TANの計算 へ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です