1
2 // 毕业论文做Pascal编译器,把编译生成的中间代码翻译为 nasm 汇编调用 C 语言库函数时,在输出 float 部分出现问题。
3
4
5 // 先说明一下,我安排栈中数据 8字节对齐,不管数据实际大小,都分配 8字节,按最低字节寻址。
6 // 当然,调用 C 语言函数时的参数栈,还是按 i386 的来。
7
8
9 // C printf 格式化字符串
10 data.add( head + "c_format_float32 : db \'%f\', 0" );
11 data.add( head + "c_format_float64 : db \'%lf\', 0" );
12 data.add( head + "c_format_float64G: db \'%G\', 0" );
13
14
15 // 栈顶已经是 8字节的 ieee754 double 数据,然后
16 text.add( line + "push dword c_format_float64G" );
17 // 或 text.add( line + "push dword c_format_float64" ); 也正确
18 text.add( head + "call printf" );
19 text.add( head + "add esp, 12" );
20 // 生成可执行文件后,运行输出正确
21
22
23 // 栈顶已经是 4字节的 ieee754 float 数据,且不等于0(次栈顶 4字节全零),然后
24 text.add( line + "push dword c_format_float32" );
25 text.add( head + "call printf" );
26 text.add( head + "add esp, 12" );
27 // 生成可执行文件后,运行输出 0.000000
28
29
30 // 正确的是
31 // 栈顶已经是 4字节的 ieee754 float 数据,然后
32 // 先把4字节的float 转为 8字节的double
33 text.add( line + "fld dword [esp]" );
34 text.add( head + "fstp qword [esp]" ); // 8字节对齐,未覆盖栈中数据
35 text.add( head + "push dword c_format_float32" );
36 text.add( head + "call printf" );
37 text.add( head + "add esp, 12" );
38 // 生成可执行文件后,运行输出正确
39
40
结论:
C 语言的 printf 使用 %f 来输出 float 时,实际上先把 4字节的float转化为 8字节的double,然后访问了栈上的 8字节数据。
(环境:Ubuntu12.04 32位 intel i3 nasm gcc)