汇编浮点数加法程序异常:为何需为浮点数分配16字节内存?
Hey there! Let's tackle your two issues step by step—first fixing the unexpected output from your assembly program, then clearing up the confusion around floating-point memory allocation.
1. Why Your Program Outputs Incorrect Results
Looking at your code, there are two key issues causing wrong output:
a. Stack Alignment for Variadic Functions
On most x86 systems (like Linux using the SysV ABI), variadic functions such as printf require the stack to be 16-byte aligned before the function call. Your current stack allocation breaks this alignment:
- When
mainstarts,%espis guaranteed to be 16-byte aligned by the caller. subl $12, %espshifts the stack pointer by 12 bytes, making it misaligned (12 isn't a multiple of 16).- Even though
pushl $.LC0adds another 4 bytes (totaling 16), the way you store the floating-point result doesn't play nicely with the aligned stack layout, leadingprintfto read incorrect memory values.
b. Simplifying the Addition Logic
Your addition using fadd %st(1), %st(0) works in theory, but it leaves the original values lingering in the FPU stack, which can lead to confusion. Using faddp (add and pop) is cleaner and ensures the result is left in %st(0) without extra register clutter.
Corrected Code
Here's a fixed version that addresses both issues and produces the expected result (18.7):
.text .globl main .type main, @function main: # Allocate 16-byte aligned stack space (8 bytes for the double result, 8 bytes padding for alignment) subl $16, %esp flds (V0) # Load single-precision V0 into %st(0) flds (V1) # Load single-precision V1 into %st(0), pushing V0 to %st(1) faddp %st(0), %st(1) # Add %st(0) to %st(1), then pop %st(0) — %st(0) now holds V0+V1 fstpl (%esp) # Store the result as an 8-byte double at the start of our allocated stack space pushl $.LC0 # Push the format string onto the stack (right-to-left argument order) call printf # Call printf: format string is top of stack, followed by the double result addl $20, %esp # Clean up stack: 16 bytes allocated + 4 bytes for format string = 20 movl $0, %eax # Return 0 from main (standard for C programs) ret .LC0: .string "%f\n" .data # Move data to the correct segment (read-write) V0: .float 9.3 V1: .float 9.4
Key improvements:
- 16-byte aligned stack allocation to meet
printf's requirements. faddpsimplifies the addition and cleans up the FPU stack.- Moved
V0andV1to the.datasegment (standard practice for data, though not strictly required for read-only values). - Added
movl $0, %eaxto return a proper exit code frommain.
2. Why Do We Need 16 Bytes of Memory for a Float?
Your confusion comes from mixing three distinct concepts:
- Raw single-precision float size: A
floatonly needs 4 bytes of memory (this is howV0andV1are stored in the.datasegment). - FPU register storage: When loaded into an x87 FPU register (
%st(n)), floats are converted to 80-bit extended-precision for calculations, but this is temporary register storage—not memory. - Stack alignment rules: The 16-byte allocation isn't for the float itself. It's to satisfy the 16-byte stack alignment requirement for variadic functions like
printf. Even though we only need 8 bytes to store the double result (required byprintf's%fspecifier), we allocate 16 bytes to keep the stack aligned. You could technically allocate 8 bytes and adjust alignment withandl $-16, %esp, but allocating 16 bytes is simpler for small programs.
To clarify: You never need 16 bytes to store a single float—the extra space is purely for adhering to stack alignment conventions when calling certain functions.
内容的提问来源于stack exchange,提问作者Qureshi

