
前回は、絶対値や符号反転など「仮数部の符号」操作を主にやりました。仮数あれば指数あり、ということで今回は指数部中心のコマケー操作です。FRNDINT(整数丸め)、FSCALE(2のべき乗スケーリング)、FEXTRACT(指数部、仮数部抽出)とな。意外と?素直な操作だけれども浮動小数のフォーマットは頭の中に置いといて。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
関連の過去回
以下の過去回で、コントロールワードCWの中の丸めや精度の制御ビットについて述べているので、忘れている「よゐこ」は御覧ください。今回のアイキャッチ画像に8087系FPUのレジスタイメージそのものといえる「テンポラリ・リアル型」のフォーマットを再掲してます。「指数部の下駄履き」とかコマケー話を忘れている場合も、参照すると良いかもです。
ぐだぐだ低レベルプログラミング(220)x86(16bit)、浮動小数点数の丸めと制御
今回練習する3命令
以下の3命令を練習します。
-
- FRNDINT
- FSCALE
- FEXTRACT
最初のFRNDINTは、スタックトップにおかれた浮動小数の小数点以下を「丸めて」整数値(でも実際はFPUレジスタ上なのでテンポラリリアルな)にしてくれるもの。その際の丸めモードは、制御レジスタCWのRCフィールドの値で決まります。デフォルトでは「近いのに丸めてね」モードです。もちろん、お好みでゼロ方向とか、+無限大方向、ー無限大方向など制御も可能。今回はやらんけど。
次のFSCALE命令は、スタックトップにある値STに対してトップ下の値ST(1)を使って以下のような2のべき乗スケーリングをした結果をスタックトップに返すもの
ST * 2 ^(ST(1))
そのとき、ST(1)は -215 ≦ ST(1) < 215 範囲の整数ということになっているみたい。
最後のFEXTRACT命令は、スタックトップにおかれた浮動小数の指数部(ただし下駄履き表現でなく、素直な指数)をST(1)に、仮数部をSTにおいてくれる命令です。なお元のスタックトップはST(1)のところです。
今回実験のプログラム
今回の「1回なでるだけ」の手抜きプログラムは以下のようです。
-
- 前回のコードをそのままコピペで、後で使うマイナス2をスタックに積む
- その上に「半端な値」127.33を積む
- FRNDINT命令を実行して、127.33の小数点以下の部分を丸める。なお、丸め設定はデフォのままなので、127になる筈。
- つづいて、FSCALE命令を発行、スタックトップ127の下にマイナス2があるので、2のマイナス2乗、つまり4分の1された結果がスタックトップに返る筈(127÷4=31.75ね。)
- 最後にFEXTRACT命令発行し、上記31.75の仮数と指数をとりだす。IEEEの浮動小数フォーマットに精通されている姉貴兄貴はどんな数が返ってくるかきっと分かっている筈。お惚け老人は怪しい、大丈夫か?
なお以下は「強力な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: fld1 fld1 faddp fchs mov bx, src1 fld dword [bx] frndint fscale fxtract fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data align=16 resb 1024 * 63 src1: dd 127.33 segment stack class=STACK align=16 resb 2048 stacktop: dw 1024 dup (0) stackend:
アセンブルして実行
MS-DOS互換で機能強化されているフリーなFreeDOS上、以下のステップで上記のアセンブラソース fscale.asm から実行可能なオブジェクトファイルを生成して実行することができます(nasmとwatcom Cがインストール済であること。)
なお、FreeDOS付属の「debugx」デバッガは、8087のレジスタ内容を浮動小数で見せてくれます。
nasm -f obj -l fscale.lst fscale.asm wlink name fscale.exe format dos file fscale.obj debugx fscale.exe
FRNDINT命令の実行前後のFPUレジスタの様子が以下に。
上記で、FRNDINT命令実行前のCW(制御レジスタの値)は黄緑枠内にあり、0x037Fです。予定どおり、丸めモードは近い方へ丸めで、精度はテンポラリリアル精度です。
FRNDINT命令実行前の値は上のST0=127.33000…の方で、実行後の値が下の赤枠ST0=127です。整数になったみたいね。
上の赤枠内の組み合わせで、127*2ー2という入力となります。浮動小数化した結果は下の黄色枠、ST0=31.75とな。
浮動小数31.75の
-
- 仮数部表現は、1.984375
- 指数部表現(下駄履きなし)は、4
ということでした。ぶっちゃけ、1.984375 * 24 を計算すれば31.75だと。あってたかな?