Linux 32位汇编浮点数加法段错误问题求助
问题分析与修复方案
咱们先把你代码里导致段错误的核心问题拆解清楚,再一步步给出解决办法:
1. 输入处理的本质错误
你用sys_read读取到opcion里的是ASCII字符串,不是FPU能直接处理的二进制浮点数!比如用户输入3.14,opcion里存的是0x33 0x2E 0x31 0x34这类ASCII码,而FPU只认符合IEEE754标准的8字节二进制浮点数格式。直接把字符串丢给FPU运算,必然会触发内存错误。
2. FPU指令的使用完全错误
x86的FPU是栈式架构,根本不支持fadd eax, ebx这种通用寄存器操作的语法。FPU指令的操作数要么是内存中的二进制浮点数,要么是FPU栈中的值。比如要做加法,得先把数据加载到FPU栈,再执行栈上的加法操作。
3. print函数的适配问题
你当前调用的print函数应该是用来打印整数或字符串的,但二进制浮点数需要先转换成ASCII字符串才能打印,直接传二进制浮点数的地址给print,要么打印乱码,要么触发错误。
修复后的代码示例
下面是修正后的完整代码,包含了字符串转二进制浮点数、FPU正确运算、浮点数转字符串打印的核心逻辑:
%include 'funciones32.asm' ; 辅助函数:字符串转double(简化实现,仅处理正数) str_to_double: push ebp mov ebp, esp push ebx ecx edx esi mov esi, [ebp+8] ; 输入字符串地址 xor ecx, ecx mov dword [int_part], 0 mov dword [frac_part], 0 mov byte [is_frac], 0 .str_loop: mov al, [esi] cmp al, 0 je .str_end cmp al, '.' je .frac_start sub al, '0' cmp byte [is_frac], 0 je .int_process .frac_process: imul dword [frac_part], 10 add [frac_part], eax jmp .next_char .int_process: imul ecx, 10 add ecx, eax jmp .next_char .frac_start: mov byte [is_frac], 1 mov [int_part], ecx xor ecx, ecx .next_char: inc esi jmp .str_loop .str_end: fild dword [int_part] cmp dword [frac_part], 0 je .no_frac fild dword [frac_part] mov ecx, 1 .frac_div: fdiv dword [ten] inc ecx cmp ecx, 7 ; 保留6位小数 jle .frac_div faddp st1, st0 .no_frac: fstp qword [ebp-8] mov eax, ebp-8 pop esi edx ecx ebx mov esp, ebp pop ebp ret ; 辅助函数:double转字符串(简化实现) double_to_str: push ebp mov ebp, esp push ebx ecx edx esi edi mov esi, [ebp+8] ; 输入double地址 mov edi, [ebp+12] ; 输出字符串地址 fld qword [esi] ; 处理整数部分 fistp dword [int_part] mov eax, [int_part] call iprint ; 用funciones32.asm的整数打印函数 ; 把整数转字符串的结果复制到edi(这里简化为直接打印,实际可完善) mov byte [edi], '.' inc edi ; 处理小数部分(保留6位) fld qword [esi] fsub dword [int_part] mov ecx, 6 .frac_loop: fmul dword [ten] fistp dword [frac_digit] mov al, [frac_digit] add al, '0' mov [edi], al inc edi fld dword [frac_digit] fsub dword [frac_digit] faddp st1, st0 loop .frac_loop mov byte [edi], 0 pop edi esi edx ecx ebx mov esp, ebp pop ebp ret SECTION .data ten dd 10 int_part dd 0 frac_part dd 0 frac_digit dd 0 is_frac db 0 prompt db "输入一个浮点数: ", 0 SECTION .bss input_str resb 32 ; 存储输入的字符串 float_bin resq 1 ; 存储转换后的二进制double output_str resb 32 ; 存储待打印的字符串 SECTION .text global _start _start: ; 提示输入 mov eax, prompt call print ; 读取输入字符串 mov eax, 3 mov ebx, 0 mov ecx, input_str mov edx, 32 int 80H ; 去掉输入末尾的换行符 mov esi, input_str .strip_newline: cmp byte [esi], 0xA jne .strip_done mov byte [esi], 0 .strip_done: ; 字符串转二进制浮点数 push input_str call str_to_double fstp qword [float_bin] ; 打印原始输入 mov eax, input_str call print call printLF ; FPU执行加法:自身加自身 fld qword [float_bin] fld qword [float_bin] faddp st1, st0 ; st0 = st0 + st1,弹出st1 ; 保存结果到内存 fstp qword [float_bin] ; 浮点数转字符串并打印 push output_str push float_bin call double_to_str mov eax, output_str call print call printLF call quit
关键知识点总结
- 输入处理:用户输入是字符串,必须先转换成IEEE754格式的二进制浮点数,才能给FPU运算。
- FPU基础用法:
fld qword [addr]:把内存中的double加载到FPU栈顶(st0)faddp st1, st0:将栈顶两个值相加,结果存在st1,然后弹出栈顶fstp qword [addr]:把栈顶数值保存到内存,再弹出栈顶
- 浮点数打印:二进制浮点数无法直接打印,必须先转换成ASCII字符串,再调用字符串打印函数。
内容的提问来源于stack exchange,提问作者José Sánchez
相关产品推荐
相关产品推荐

