NASM x64程序提取浮点数小数部分错误显示0.000000求助
提取浮点数小数部分时输出0.0而非0.5的问题
问题描述
在Windows 10 x64环境下使用NASM编写汇编程序,意图提取并打印浮点数的小数部分,但程序输出Floating Point Part: 0.000000,而非预期的Floating Point Part: 0.500000。
原代码
section .data fmt db "Floating Point Part: %f", 10, 0 float_value dq 10.5 result dq 0.0 section .text global main extern printf main: ; Function prologue push rbp mov rbp, rsp sub rsp, 32 ; Allocate shadow space ; Load floating-point value into the FPU fld qword [rel float_value] ; Call floatVal function call floatVal ; Store result from the FPU to memory fstp qword [rel result] ; Print the result using printf lea rcx, [rel fmt] ; First argument: format string lea rdx, [rel result] ; Second argument: address of result movsd xmm0, qword [rdx] ; Move the double to xmm0 for printf call printf ; Function epilogue add rsp, 32 ; Deallocate shadow space mov eax, 0 pop rbp ret floatVal: ; Function prologue push rbp mov rbp, rsp sub rsp, 32 ; Allocate shadow space ; Get the integer part fld st0 ; Copy the value to the top of the stack frndint ; Round to integer fsub ; Subtract integer part from original value (st0 - st1) ; Function epilogue add rsp, 32 ; Deallocate shadow space pop rbp ret
编译执行步骤
nasm -f win64 flo.asm -o flo.o gcc -m64 -o flo flo.o -lkernel32 -lmsvcrt .\flo.exe
预期与实际输出
- 预期输出:
Floating Point Part: 0.500000 - 实际输出:
Floating Point Part: 0.000000
环境
- Windows 10 x64
- NASM
- Mingw-w64
已尝试操作
- 确认浮点运算处理正确
- 检查栈对齐与shadow space分配
- 尝试直接打印完整浮点数以定位问题
- 使用RIP相对寻址确保地址处理正确
疑问
- 为何程序打印0.000000而非预期的小数部分?
- 浮点处理或函数调用约定是否存在错误?
- 如何正确打印浮点数的小数部分?
问题根源
代码存在两个关键错误:
- printf调用未设置eax寄存器:Windows x64调用约定中,调用
printf这类可变参数函数时,必须在eax中指定通过XMM寄存器传递的浮点参数数量。原代码设置mov eax, 0,导致printf认为没有浮点参数,无法读取xmm0中的有效值,转而读取栈上的无效数据(表现为0.0)。 - FPU减法指令逻辑模糊:无操作数的
fsub指令实际执行st1 = st1 - st0并弹出st0,虽能得到结果,但逻辑不够直观,且未明确维护FPU栈平衡。 - 多余的参数传递:调用
printf时错误将rdx设为result的地址,%f参数已通过xmm0传递,该操作完全多余。
修正后的代码
section .data fmt db "Floating Point Part: %f", 10, 0 float_value dq 10.5 result dq 0.0 section .text global main extern printf main: ; Function prologue push rbp mov rbp, rsp sub rsp, 32 ; Allocate shadow space ; Load floating-point value into the FPU fld qword [rel float_value] ; Call floatVal function call floatVal ; Store result from the FPU to memory fstp qword [rel result] ; Print the result using printf lea rcx, [rel fmt] ; First argument: format string movsd xmm0, qword [rel result] ; Move the double to xmm0 for printf mov eax, 1 ; 告知printf使用了1个XMM浮点参数 call printf ; Function epilogue add rsp, 32 ; Deallocate shadow space mov eax, 0 pop rbp ret floatVal: ; Function prologue push rbp mov rbp, rsp ; Get the integer part fld st0 ; Copy the value to the top of the stack frndint ; Round to integer fsubr st0, st1 ; st0 = st1 - st0 (原数 - 整数部分) fstp st1 ; 弹出原数,栈顶保留小数部分 ; Function epilogue pop rbp ret
修正说明
- printf调用修正:移除多余的
lea rdx, [rel result],添加mov eax, 1告知printf使用了1个XMM浮点参数。 - FPU运算修正:使用
fsubr st0, st1直接计算原数减整数部分,逻辑更清晰;用fstp st1清理FPU栈,确保函数返回时栈深度与进入时一致。 - 优化shadow space:
floatVal函数未调用其他函数,无需分配shadow space,移除相关代码。
验证结果
使用原编译步骤重新执行,将得到预期输出:
Floating Point Part: 0.500000
内容的提问来源于stack exchange,提问作者kavi castelo
相关产品推荐
相关产品推荐

