在程序集 linux 中将浮点值打印到 STDOUT [英] Printing Float Value to STDOUT in assembly linux
问题描述
我正在尝试将浮点值打印到控制台,但它给出了意外的结果,因为数字太大且符号不同.
计划
.datafloat_: .asciz "%f\n"n1: .float 10.4n2: .float 10.3.文本.globl 主主要的:低于 1 美元,%esp结束flds n1fsubs n2fsts 1(%esp)movl 1(%esp), %eax呼叫 pfladdl $1, %esp.出口:movl $1, %eaxmovl $0, %ebx整数 $0x80pfl:推动推 %eax推$浮动_调用 printf加 8 美元,%esp流行的回复
输出
每次都不同,但介于 -400...0.0... 到 -500000..0.0...
您的代码存在各种问题.最重要的是,%f
需要一个 double
,但您传递的是 float
.请参阅 man 3 printf 或 C 书籍.此外,float
是 4 个字节,因此建议您分配 4 个字节而不是 1.更糟糕的是,您甚至没有使用分配的 1 个字节,因为它位于 (%esp)
但你使用了 1(%esp)
.对于双精度型,您将需要 8 个字节.接下来,您忘记从 FPU 弹出值.此外,当前的调用约定需要 16 字节对齐的堆栈,即使在 32 位模式下也是如此.
最后,不建议在main
或其他使用libc函数的代码中直接使用exit系统调用.相反,如果您真的坚持要确保 libc 清理(如刷新 stdio 缓冲区)发生,则只需 ret
或 call exit
.否则,如果您将 stdout 重定向到一个文件,因此它是全缓冲的,您将不会得到任何输出.
这是修复上述所有问题的可能版本:
.datafloat_: .asciz "%f\n"n1: .float 10.4n2: .float 10.3.文本.globl 主主要的:sub $12, %esp # 8 字节用于双精度,+ 4 字节用于对齐flds n1fsubs n2fstpl (%esp) # 从 fpu 弹出 doublemovl (%esp), %eax # 低 4 字节movl 4(%esp), %edx # 高 4 字节呼叫 pfl加 12 美元,%esp回复## 输入:EDX:EAX = 双精度位模式pfl:推 %edx推 %eaxpush $float_ # 3x push 再次将堆栈重新对齐 16调用 printf加 12 美元,%esp回复
通过整数寄存器来回弹你的 double
是不必要的;如果您将 call printf
内联到您的主函数中,您可以只使用 fstpl
将 double
放在堆栈中指向格式指针的正上方细绳.
或者让你的 pfl
函数在 %st(0)
中接受它的输入,而不是整数寄存器,因为无论如何你都在制定自定义调用约定.>
(我假设您的下一个问题将是为什么它打印 0.099999
而不是 0.1
:))
I'm trying to print float values to console but it gives unexpected results too large number having different sign.
Program
.data
float_: .asciz "%f\n"
n1: .float 10.4
n2: .float 10.3
.text
.globl main
main:
sub $1, %esp
finit
flds n1
fsubs n2
fsts 1(%esp)
movl 1(%esp), %eax
call pfl
addl $1, %esp
.exit:
movl $1, %eax
movl $0, %ebx
int $0x80
pfl:
pushal
push %eax
push $float_
call printf
add $8, %esp
popal
ret
Output
Different every time but between -400...0.0... to -500000..0.0...
Various problems with your code. Most importantly, %f
expects a double
but you pass a float
. See man 3 printf or a C book. Furthermore a float
is 4 bytes so you'd be well advised to allocate 4 bytes not 1. To make matters worse, you are not even using the 1 byte allocated since that is at (%esp)
but you used 1(%esp)
. For a double you will need 8 bytes. Next, you forgot to pop the value from the FPU. Also, current calling conventions require 16 byte aligned stack, even in 32 bit mode.
Finally, it's not recommended to use the exit system call directly in main
or other code that uses libc function. Instead, just ret
or call exit
if you really insist to make sure libc cleanup (like flushing stdio buffers) happens. Otherwise you'll get no output if you redirect stdout to a file so it's full-buffered.
Here is a possible version fixing all of the above:
.data
float_: .asciz "%f\n"
n1: .float 10.4
n2: .float 10.3
.text
.globl main
main:
sub $12, %esp # 8 bytes for double, + 4 bytes for alignment
flds n1
fsubs n2
fstpl (%esp) # pop double from fpu
movl (%esp), %eax # low 4 bytes
movl 4(%esp), %edx # high 4 bytes
call pfl
addl $12, %esp
ret
## input: EDX:EAX = the bit-pattern for a double
pfl:
push %edx
push %eax
push $float_ # 3x push realigns the stack by 16 again
call printf
add $12, %esp
ret
Bouncing your double
through integer registers is unnecessary; if you inlined call printf
into your main function you could have just used fstpl
to put the double
on the stack right above a pointer to the format string.
Or made your pfl
function takes its input in %st(0)
instead of integer registers, since you're making up a custom calling convention anyway.
(I assume your next question is going to be why it prints 0.099999
instead of 0.1
:))
这篇关于在程序集 linux 中将浮点值打印到 STDOUT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!