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

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

ただいまの
回答率

90.38%

  • アセンブリ言語

    117questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 842

binary

score 16

アセンブリ言語で小数を扱うプログラムを書いていて、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と同じでした。
回答よろしくお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

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/11 08:30

    printfは可変長引数なので、既定の実引数拡張が入ってfloatもdoubleになります(規格上決まった動作です)。

    http://www.kijineko.co.jp/tech/superstitions/printf-format-for-double.html

    キャンセル

  • 2018/03/13 13:57

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

    キャンセル

  • 2018/03/13 15:49

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

    キャンセル

  • 2018/03/14 13:49

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

    キャンセル

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で表示します。

$ cat t4_test1.c
#include <stdio.h>

int main(void) {
  unsigned char bd[] = { 0xcd, 0x3b, 0x7f, 0x66, 0x9e, 0xa0, 0xf6, 0x3f };

  double d1 = *((double*)bd);
  printf("d1 = [%f]\n", d1);
  return 0;
}

$ gcc -Wall t4_test1.c

$ ./a.exe
d1 = [1.414214]

$

正しく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/13 13:51

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

    キャンセル

  • 2018/03/13 14: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バイト)になっているので、正しい表示がされる、と言う訳です。この流れだとすると見せ方が変化していると言えます。

    キャンセル

  • 2018/03/13 15:19

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

    キャンセル

  • 2018/03/14 13:51

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

    キャンセル

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

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • アセンブリ言語

    117questions

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