Printing floating point numbers in assembler
for your assembler problem:
you need to align the stack before your main program starts.
insert
sub rsp, 8
right after main:
then add it again before ret:
add rsp, 8
How to print floating point numbers from assembly?
You need to add a size suffix to your floating point operations if they happen to use memory operands. Otherwise, the GNU assembler will implicitly use single precision which is not what you want. To fix your code, change
fmul pi # multiply r^2 by pi
fst pi # Store result to pi
to
fmull pi # multiply r^2 by pi
fstl pi # Store result to pi
Some other remarks about your code:
use
rip
-relative addressing modes instead of absolute addressing modes if possible. Specifically, this means to replacefoo
withfoo(%rip)
in your memory operands, including forlea result(%rip), %rdi
make sure to leave a clean x87 stack at the end of your functions or other code may spuriously cause it to overflow. For example, use
fstpl pi(%rip)
to store the result and pop it off the stack.use
movsd
, notmovupd
to load one double into an SSE register, not a pair.consider using SSE instead of x87 if possible for all the math. It's the standard way to do scalar FP math in x86-64, that's why XMM registers are part of the calling convention. (Unless you need 80-bit extended precision, but you have a
pi
constant in memory that's far less accurate than x87fldpi
.)...
cvtsi2sd %rbx, %xmm0
mulsd pi(%rip), %xmm0
ret
Printing floating point numbers from x86-64 seems to require %rbp to be saved
I suspect the problem doesn't have anything to do with %rbp
, but rather has to do with stack alignment. To quote the ABI:
The ABI requires that stack frames be aligned on 16-byte boundaries. Specifically, the end of
the argument area (%rbp+16) must be a multiple of 16. This requirement means that the frame
size should be padded out to a multiple of 16 bytes.
The stack is aligned when you enter main()
. Calling printf()
pushes the return address onto the stack, moving the stack pointer by 8 bytes. You restore the alignment by pushing another eight bytes onto the stack (which happen to be %rbp
but could just as easily be something else).
Here is the code that gcc
generates (also on the Godbolt compiler explorer):
.LC1:
.ascii "%10.4f\12\0"
main:
leaq .LC1(%rip), %rdi # format string address
subq $8, %rsp ### align the stack by 16 before a CALL
movl $1, %eax ### 1 FP arg being passed in a register to a variadic function
movsd .LC0(%rip), %xmm0 # load the double itself
call printf
xorl %eax, %eax # return 0 from main
addq $8, %rsp
ret
As you can see, it deals with the alignment requirements by subtracting 8 from %rsp
at the start, and adding it back at the end.
You could instead do a dummy push/pop of whatever register you like instead of manipulating %rsp
directly; some compilers do use a dummy push to align the stack because this can actually be cheaper on modern CPUs, and saves code size.
How to print float number in SASM?
%include "io.inc"
section .data
msg: db "Printing float number %f",0 ;format for print string
val: dq 2.45 ;64 bit floating point
section .bss
res: resq 1
section .text
global CMAIN
CMAIN:
mov ebp, esp; for correct debugging
;write your code here
fld qword[val] ;need to convert 32 bit to 64 bit
fstp qword[res] ;floating load makes 80 bit
;store as 64 bit
;push last argument first
push dword[val+4] ;64 bit floating point (bottom)
push dword[val] ;64 bit floating point (top)
push dword[res+4] ;64 bit floating point (bottom)
push dword[res] ;64 bit floating point (top)
push dword msg ;adress of format string
call printf
add esp,20 ;pop stack
mov eax,0 ; exit code, 0=normal
xor eax, eax
ret
How to print a single-precision float with printf
printf(3)
's %f
format specifier wants a double
.
There is no way to get printf to accept a float
, only double
or long double
.
C's default argument promotions specify that calls to variadic functions like foo(char *fmt, ...)
promote float
to double
, and perform the usual integer promotions of narrow integer types to int
, for trailing args that match the ...
part of the prototype. (The same applies to all args for calling functions with no prototype.) N1570 6.5.2.2 Function calls, subsections 6 and 7.
Thus C provides no way for a caller to pass a float
to printf
, so it has no conversion for it. %f
means double
. %lf
also works for double
in modern printf implementations, C99/C11 and C++11. You can safely use the same %lf
format string with a double
for printf
and scanf
.
Note that scanf
is different. float *
and double *
aren't affected by those promotions, so you can actually scan into a float
with %f
.
Load with CVTSS2SD .num(%rip), %xmm0
If you look at compiler output, you'll see gcc do everything you did. It uses RIP-relative addressing for static storage as usual.
GCC also uses pxor
to zero the register first to break the false dependency on the old value of %xmm0
. (cvtss2sd
's poor design leaves the upper 64 bits of the destination unchanged.) GCC errs on the side of caution, and inserts xor-zeroing instructions to break false dependencies in many cases.
You're probably getting 0 because the upper bits of xmm0 happen to be zero. When printf
looks at the low 64 bits of xmm0 as a double
(IEEE binary64 on x86), it finds the bit pattern for 123.4f
in the low 32 bits of the mantissa, and the rest zero. As a 64-bit double
, this bit-pattern represents a very small (subnormal) number, so it comes out as zero with %f
.
You can try the equivalent with a float
, (e.g. on http://www.h-schmidt.net/FloatConverter/IEEE754.html), setting some bits in the low half to see what you get.
If you used %g
(scientific notation) or %a
(hex representation of the double
bit-pattern), the non-zero bits would show up. (Unless maybe if you had Denormals Are Zero mode enabled in the MXCSR, although glibc might use purely integer stuff to pick apart FP bit-patterns when converting to base-10 strings; it's a hard problem.)
Related Topics
How to Trigger a Function in Kernel Module Interrupt
Gcc: Linked Libraries in /Usr/Local/Lib Are Not Found, But /Etc/Ld/So.Conf.D/Libc.Conf Lists It
Centos Cgconfig Fails to Start
Running a Program Through Ssh Fails with "Error Opening Terminal: Unknown."
Ffmpeg Combining Images to Video and Streaming in One Command Line
What Is Difference Between Sched_Batch and Sched_Other Scheduling
How to Use Xdotool to Enter a Web Console Command
Remove Git-Annex Repository from File Tree
Why This Shell Won't Work If It's Called from Rc.Local But Ssh
How to Read a Value from User Input into a Variable
How to Do an Initial Setup of Slapd Olc with Ldapmodify
How Does Apparmor Do "Environment Scrubbing"
Why Does Cat <<< $Var1 Lose Newlines
Linux - Bash Redirect a String to a File
How to Know If I Can Compile with Fma Instruction Sets
Ssl/Qsslsocket_Openssl.Cpp:1414: Error: Q_Ssl_Ctrl Was Not Declared in This Scope Error