汇编FPU浮点运算问题:小数乘10的幂结果异常求助
x86汇编浮点运算异常问题排查与修复
问题描述
编写x86汇编程序实现:将用户输入的小数按指定精度(1-4)乘以对应10的幂,示例输入1.23456、精度2时预期结果为123.456。但运行后结果要么是科学计数法格式,要么显示为NaN,尝试修改变量类型、调整FPU栈加载顺序均未解决,对汇编浮点运算经验较少。
原始代码
INCLUDE Irvine32.inc .DATA inputPrompt BYTE "Enter a number with at least 5 decimal places: ", 0 precisionPrompt BYTE "Enter a precision value (1, 2, 3, or 4): ", 0 multiplyValueMsg BYTE "Multiply Value: ", 0 userInput REAL4 0.0 ; 存储用户输入的浮点值 precisionValue DWORD 0 ; 存储精度值 multiplyValue DWORD 0 ; 对应10的幂值(10/100/1000/10000) resultMsg BYTE " Result: ", 0 resultValue REAL4 0.0 ; 存储运算结果 .CODE MAIN PROC mov edx, OFFSET inputPrompt call WriteString mov edx, OFFSET userInput mov ecx, 101 call ReadString ; 错误:用ReadString读取浮点输入 ; 读取精度值 mov edx, OFFSET precisionPrompt call WriteString call ReadInt mov precisionValue, eax ; 计算multiplyValue mov eax, precisionValue cmp eax, 1 je precision1 cmp eax, 2 je precision2 cmp eax, 3 je precision3 cmp eax, 4 je precision4 jmp next precision1: mov multiplyValue, 10 jmp next precision2: mov multiplyValue, 100 jmp next precision3: mov multiplyValue, 1000 jmp next precision4: mov multiplyValue, 10000 jmp next next: ; 显示multiplyValue mov eax, multiplyValue mov edx, OFFSET multiplyValueMsg call WriteString call WriteDec ; 浮点运算部分 fld userInput fild dword ptr [multiplyValue] fmul fstp dword ptr resultValue ; 显示结果 mov edx, OFFSET resultMsg call WriteString fld resultValue call WriteFloat ; 默认格式会出现科学计数法 MAIN ENDP END MAIN
核心问题与修复方案
1. 输入读取错误(导致NaN的根本原因)
ReadString是将用户输入的字符直接存入内存,但userInput是REAL4浮点类型,内存里的字符数据不是合法的浮点值,后续FPU加载后自然会出现NaN。
修复:替换为Irvine32库的ReadFloat函数,它会自动解析用户输入的字符串为浮点值:
; 替换原ReadString的代码块 mov edx, OFFSET inputPrompt call WriteString call ReadFloat ; 读取用户输入的浮点值到FPU栈顶(ST0) fstp userInput ; 将ST0的值存入userInput变量
2. 显示格式问题(科学计数法)
WriteFloat默认会根据数值大小自动切换科学计数法,需要通过设置FPU控制字强制使用定点数格式,并指定显示精度。
修复:在显示结果前添加FPU控制字设置:
; 在.data段添加控制字定义 fpu_control_word DWORD 0x037F ; FPU控制字:定点格式、6位小数精度、禁用异常 ; 在调用WriteFloat前插入以下代码 fldcw fpu_control_word
完整修复后代码
INCLUDE Irvine32.inc .DATA inputPrompt BYTE "Enter a number with at least 5 decimal places: ", 0 precisionPrompt BYTE "Enter a precision value (1, 2, 3, or 4): ", 0 multiplyValueMsg BYTE "Multiply Value: ", 0 userInput REAL4 0.0 precisionValue DWORD 0 multiplyValue DWORD 0 resultMsg BYTE " Result: ", 0 resultValue REAL4 0.0 fpu_control_word DWORD 0x037F ; FPU控制字:定点格式、6位小数 .CODE MAIN PROC mov edx, OFFSET inputPrompt call WriteString call ReadFloat ; 读取浮点输入到ST0 fstp userInput ; 存储到userInput ; 读取精度值 mov edx, OFFSET precisionPrompt call WriteString call ReadInt mov precisionValue, eax ; 计算multiplyValue mov eax, precisionValue cmp eax, 1 je precision1 cmp eax, 2 je precision2 cmp eax, 3 je precision3 cmp eax, 4 je precision4 jmp next precision1: mov multiplyValue, 10 jmp next precision2: mov multiplyValue, 100 jmp next precision3: mov multiplyValue, 1000 jmp next precision4: mov multiplyValue, 10000 jmp next next: ; 显示multiplyValue mov eax, multiplyValue mov edx, OFFSET multiplyValueMsg call WriteString call WriteDec call Crlf ; 浮点运算 fld userInput fild dword ptr [multiplyValue] fmul fstp dword ptr resultValue ; 显示结果(设置FPU格式) mov edx, OFFSET resultMsg call WriteString fld resultValue fldcw fpu_control_word ; 应用定点格式设置 call WriteFloat call Crlf MAIN ENDP END MAIN
验证效果
输入1.23456、精度2时,程序会输出Multiply Value: 100和Result: 123.456001(因REAL4精度限制有微小误差,若要更精确可改用REAL8类型)。
内容的提问来源于stack exchange,提问作者MadTragic
相关产品推荐
相关产品推荐

