質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

Q&A

解決済

2回答

4575閲覧

アセンブリ言語 小数について

binary

総合スコア33

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

0グッド

0クリップ

投稿2018/03/10 08:03

アセンブリ言語で小数を扱うプログラムを書いていて、FPUデータレジスタに積んでいる小数を普通のスタックに積む段階でプログラムが不明な動きをしたので質問します。
問題のプログラムは

.bss .comm buffer, 64 .data code: .ascii "%f\n\0" .text .globl _main _main: pushl %ebp movl %esp, %ebp subl $64, %esp movl $2, %eax movl %eax, -4(%ebp) movl $buffer, %esi fildl -4(%ebp) fsqrt fstl 4(%esp) movl $code, (%esp) call _printf fstl (%esi) pushl (%esi) pushl $code call _printf leave ret

このプログラムの実行結果は
1.414214
0.000000
です。
質問なのですが.commのディレクティブ作ったBSSデータセクションには浮動小点数を保存することはできないのですか?
また、小数を4(%esp)に保存しています。なので、gdbのメモリの中を見るx/fで%esp+4のメモリ空間を見たのですが、小数は保存されていませんでした。小数の代わりに桁がとても大きな数字が入っていました。しかし、プログラムを走らせてprintfを実行すると標準出力されるのはでう言う事なのでしょうか。念の為、display/x $espを使い%espレジスタの値を調べその値に4を加算してその値のメモリ空間を調べましたが結果はx/f $espと同じでした。
回答よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

.commのディレクティブ作ったBSSデータセクションには浮動小点数を保存することはできないのですか?

できます。と、申しますか、メモリ(ここではBSSセクション)上ではその領域が浮動小数点数なのか整数なのか、はたまた固定小数点数なのかの区別はありません。あくまで、取り出した値をレジスタなり演算なりで扱うときに浮動小数点数として扱うか、整数として扱うかの差だけです。言い方を変えるとプログラマ次第です。デバッガーであるgdbの見せ方とも言えます。

質問者様の例では、恐らく、gdbでメモリをダンプしている場所が間違っているか、あるいは浮動小数点数の扱いについて少し誤解があるのかもしれません。小数(つまり実数)はお試しのプログラムでは64ビットの倍精度浮動小数点数で扱っています。これは、メモリ上にストアすると浮動小数点数のビットパターンになります。

ご提示のasmプログラムをcygwin(32ビット)上で確認しました。gccのバージョンは6.4.0です。

gdbでの実行例を示してみます。gdb で、fidl -4(%ebp)の実行直後を見てみましょう。

まず手始めに、-4(%ebp)を4バイト分ダンプして、正しく2の整数がロードされるはずか確認してみます。

16 fildl -4(%ebp) (gdb) n 17 fsqrt (gdb) x/4bx $ebp-4 0x28cc44: 0x02 0x00 0x00 0x00

4バイト(32ビット)分、リトルエンディアンでメモリに整数の2が格納されているので、正しくロードしたはずです。
ここで、浮動小数点数用のFPUのデータレジスタにロードされているかinfo floatコマンドで確認します。

(gdb) info float =>R7: Valid 0x40008000000000000000 +2 R6: Empty 0x00000000000000000000 R5: Empty 0x00000000000000000000 R4: Empty 0x00000000000000000000 R3: Empty 0x00000000000000000000 R2: Empty 0x00000000000000000000 R1: Empty 0x00000000000000000000 R0: Empty 0x00000000000000000000 Status Word: 0x3800 TOP: 7 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x3fff Instruction Pointer: 0x23:0x004011e3 Operand Pointer: 0x2b:0x0028cc44

R7 に整数の2がロードされていることが分かります。0x40008000000000000000 となっているのは
2を80ビットの16進数表記、浮動小数点数で表されているからです。

ちなみになぜ80ビットなのと言うと、試しているマシンのCPUがインテルCore i7 870 で、FPUが8087ベースで80ビットだからです。

続けてfsqrt 命令を実行します。2の平行根が得られるはずです。

(gdb) n 18 fstl 4(%esp) (gdb) info float =>R7: Valid 0x3fffb504f333f9de6484 +1.414213562373095049 R6: Empty 0x00000000000000000000 R5: Empty 0x00000000000000000000 R4: Empty 0x00000000000000000000 R3: Empty 0x00000000000000000000 R2: Empty 0x00000000000000000000 R1: Empty 0x00000000000000000000 R0: Empty 0x00000000000000000000 Status Word: 0x3820 PE TOP: 7 Control Word: 0x037f IM DM ZM OM UM PM PC: Extended Precision (64-bits) RC: Round to nearest Tag Word: 0x3fff Instruction Pointer: 0x23:0x004011e6 Operand Pointer: 0x2b:0x0028cc44 Opcode: 0xd9fa

R7の値が変わっています。+1.414213562373095049となっていて、gdbにより整数2の平方根が実数で表示されていることが分かります。

更に続けて fstl 4(%esp) を実行します。64ビット(8バイト)の浮動小数点数が4(%esp)に収まります。
それを8バイト分ダンプします。

18 fstl 4(%esp) (gdb) n 19 movl $code, (%esp) (gdb) x/8xb $esp+4 0x28cc0c: 0xcd 0x3b 0x7f 0x66 0x9e 0xa0 0xf6 0x3f

これを実数で表示してみると

(gdb) x/f $esp+4 0x28cc0c: 1.4142135623730951 (gdb)

となり、1.414213... で正しく2の平方根が表示されます。
FPUのデータレジスタは80ビット(10バイト)なのだけど、fst命令でメモリへストアされるとき、64ビットの倍精度浮動小数点数に変換されます。その為、info float でR7を見たときの値とは異なることになります。

上記で、bssセクションのbuffer に正しく値が格納されていることが分かったと思います。printfで実行して正しく表示されるのは間違いないです。

参考:AP-943 FPU、SSE、および SSE2 を使用した浮動小数点算術演算

おまけとして、8バイトの0xcd, 0x3b, ...の8バイトのシーケンスが本当に浮動小数点数として扱われているか、C言語のプログラムで確認した例を示します。メモリ上のバイト列を無理やりdouble型変数に格納して、printfで表示します。

C

1$ cat t4_test1.c 2#include <stdio.h> 3 4int main(void) { 5 unsigned char bd[] = { 0xcd, 0x3b, 0x7f, 0x66, 0x9e, 0xa0, 0xf6, 0x3f }; 6 7 double d1 = *((double*)bd); 8 printf("d1 = [%f]\n", d1); 9 return 0; 10} 11 12$ gcc -Wall t4_test1.c 13 14$ ./a.exe 15d1 = [1.414214] 16 17$

正しく2の平方根である1.41421...が表示されました。


2018/03/13 追記しました。

コメントをいただいたのでこちらでも検証しました。不正な(期待しない)値が表示されたのはコメントでの推測のとおり、gdbのxコマンドの挙動によるもので、メモリ上の対象範囲の解釈の違いが原因です。
結論としては「gdbの使い方の問題」と言えます。

以下のとおり、トレースしてみます。
当該プログラムをrunし、fstl 4(%esp)の実行直後でブレイクします。

(gdb) 18 fstl 4(%esp) (gdb) 19 movl $code, (%esp)

ここでx/fコマンド実行。期待した値(正しいのは1.1414...)が表示されていません。

(gdb) x/f $esp+4 0x28cc0c: 3.01326646e+23

x/8xbで8バイト分ダンプします。メモリ上の8バイトは正しいことが分かります。

(gdb) x/8xb $esp+4 0x28cc0c: 0xcd 0x3b 0x7f 0x66 0x9e 0xa0 0xf6 0x3f

続けてx/fを実行。直前でサイズ指定した為にそのサイズがデフォルト値として利用されるので、正しく表示されます。

(gdb) x/f $esp+4 0x28cc0c: 1.4142135623730951

おまけとして、「サイズ指定すればいいのか」と言うことで、x/f にサイズ指定(g=8バイト/ w=(4バイト)して実行してみます。

(gdb) x/fg $esp+4 0x28cc0c: 1.4142135623730951 (gdb) x/fw $esp+4 0x28cc0c: 3.01326646e+23 (gdb)

x/fwではダメなことが分かります。

投稿2018/03/11 04:47

編集2018/03/13 06:25
dodox86

総合スコア9183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

binary

2018/03/13 04:51

回答ありがとうございます。 gdbでもう一度プログラムを走らせて確認しました。 x/fで小数が格納されているはずのメモリを見るとやはり、格納されていたのは変な値でした。その後、x/8xbを実行してからx/fで小数を確認すると値が期待通りの値になっていました。 これはx/8xbを実行する前と後でgdbの数字の見せ方が変化したと言うことなのでしょうか?
dodox86

2018/03/13 05:30

> これはx/8xbを実行する前と後でgdbの数字の見せ方が変化したと言うことなのでしょうか? それかもしれませんね。binaryさんが入力したコマンドの詳細によりますが、 xコマンドのオプションがデフォルト値で解釈されているかもしれません。 http://flex.phys.tohoku.ac.jp/texi/gdb-j/gdb-j_41.html “x/f”は、浮動小数点数での表示ですが、サイズ指定においてwで4バイト、gで8バイト分をダンプします。更にアドレスも指定しますが、サイズもアドレスも未指定だと、デフォルト値を利用します。最初の”x/f”で デフォルトサイズのw(4バイト)(=gdbにおけるワード)で解釈され、ダンプすると間違った値で表示されます。その後、”x/8xb”でデフォルトサイズがg(8バイト)にセットされ、 更に次に”x/f” にすると今度はデフォルトサイズがg(8バイト)になっているので、正しい表示がされる、と言う訳です。この流れだとすると見せ方が変化していると言えます。
dodox86

2018/03/13 06:19

コメントを受けて再度検証してみましたので、回答欄の追記をご覧ください。
binary

2018/03/14 04:51

回答ありがとうございました。 gdbをまだぜんぜん使いこなせてないとわかりました。 多くのデバッグを通して学んでいきたいと思います。
guest

0

ベストアンサー

浮動少数命令でのlサフィックスは倍精度を表します。

fsts (%esi) pushl (%esi) pushl $code call _printf

で、それっぽくはなるんですが
まだ問題があります。
Cの規格上だったのか実装上だったかはちょっと失念したのですが
printfで"%f"フォーマットを指定しても結果的に倍精度浮動小数点数を受け渡し表示しています。

bufferに倍精度を保存するなら

fstl (%esi) pushl 4(%esi) pushl (%esi) pushl $code call _printf

bufferに単精度を保存するなら

fsts (%esi) flds (%esi) fstl 4(%esp) movl $code, (%esp) call _printf

投稿2018/03/10 15:51

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

binary

2018/03/13 04:57

回答ありがとうございました。 ご提示していただいたプログラムを参考に修正するとbssデータセクションの値が出力されていました。 プログラムで質問があるのですが、プログラムのこのはじめの2回のpushは、格納された小数がスタックメモリ空間にダブルワードの形でメモリ空間を跨って格納されていてそのために2回のpush命令が必要だったと言う解釈で大丈夫でしょうか? pushl 4(%esi) pushl (%esi) pushl $code call _printf
asm

2018/03/13 06:49

倍精度浮動小数点数は8byte必要で pushlは4byteをスタックに積む命令なので2回に分ける必要がありました。
binary

2018/03/14 04:49

回答ありがとうございました。 良い勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問