MIPS浮点数与双精度浮点数比较输出异常问题排查
MIPS浮点数最大值计算的问题与修复
问题描述
在MIPS汇编中实现单/双精度浮点数最大值打印时,出现以下异常:
- 单精度测试时,输入
1.00000001和1.0,输出结果为1.00000000而非预期的1.00000001;若输入1.1和1.0,输出1.10000002而非1.10000000 - 双精度测试时,输入
1.00000001和1.0,输出极小值5.2676887711382507e-315而非预期的1.00000001
原代码如下:
# maxFloat.s .data msg: .asciiz "The larger number is " newline: .asciiz "\n" num1: .float 1.00000001 num2: .float 1.0 num3: .double 1.00000001 num4: .double 1.0 .text .globl main main: la $a0, num1 # storing num1's address la $a2, num2 # storing num2's address jal maxFloat # jumps to maxFloat function li $v0, 4 # syscall 4 (print msg) la $a0, msg # argument: string syscall # print the string li $v0, 2 # syscall 2 (print float) # argument: $f12 (float) syscall # print the float li $v0, 4 # syscall 4 (print new line) la $a0, newline # argument: string syscall # print the string # Now do the same thing for doubles # la $a0, num3 # storing num3's address la $a2, num4 # storing num4's address jal maxDouble # jumps to maxDouble function li $v0, 4 # syscall 4 (print msg) la $a0, msg # argument: string syscall # print the string li $v0, 3 # syscall 3 (print double) # argument: $f12 (double) syscall # print the double li $v0, 4 # syscall 4 (print new line) la $a0, newline # argument: string syscall # print the string li $v0, 10 # syscall 10 (exit) syscall maxFloat: l.s $f0, ($a0) # load num1 l.s $f2, ($a2) # load num2 mov.s $f12, $f2 # default to returning num2 c.lt.s $f0, $f2 # if num1 < num2, do nothing bc1t skipF mov.s $f12, $f0 # if num1 >= num2, return num1 skipF: jr $ra # return to caller maxDouble: l.d $f0, ($a0) # load num3 l.d $f2, ($a2) # load num4 mov.d $f12, $f2 # default to returning num2 c.lt.d $f0, $f2 # if num3 < num4, do nothing bc1t skipD mov.d $f12, $f0 # if num3 >= num4, return num3 skipD: jr $ra # return to caller
问题根源与修复方案
1. 单精度结果异常:浮点数精度限制
单精度浮点数仅能保留约7位十进制有效数字,1.00000001有8位有效数字,超出了单精度的精确表示范围。编译器会将其舍入到最接近的可表示单精度值,该值与1.0完全相等,因此比较逻辑会返回1.0。
若要验证单精度比较逻辑,需使用差异足够大的数值(如1.5和1.0),此时能正确返回较大值。另外,1.1这类十进制小数无法被二进制浮点数精确表示,输出1.10000002是单精度下的近似值,属于浮点数的固有特性,无法消除。
2. 双精度输出极小值:比较逻辑与寄存器优化
双精度异常的核心原因是浮点比较的跳转逻辑存在隐含的寄存器冲突风险,同时原逻辑在处理相等值时的行为不够明确。修复后的maxDouble函数调整了默认返回值和跳转逻辑,确保寄存器操作的正确性:
maxDouble: l.d $f0, ($a0) # load num3 l.d $f2, ($a2) # load num4 mov.d $f12, $f0 # 默认返回num3 c.lt.d $f0, $f2 # 若num3 < num4,则切换为返回num2 bc1t skipD jr $ra # 否则直接返回num3 skipD: mov.d $f12, $f2 # 返回num2 jr $ra # 返回调用者
此外,需确保模拟器(如SPIM/MARS)启用了浮点单元支持,多数现代模拟器会默认启用该功能。
修复后的完整代码
# maxFloat_fixed.s .data msg: .asciiz "The larger number is " newline: .asciiz "\n" # 单精度测试用例:使用差异明显的数值验证逻辑 num1: .float 1.5 num2: .float 1.0 # 双精度测试用例 num3: .double 1.00000001 num4: .double 1.0 .text .globl main main: la $a0, num1 la $a2, num2 jal maxFloat li $v0, 4 la $a0, msg syscall li $v0, 2 syscall li $v0, 4 la $a0, newline syscall # 双精度处理 la $a0, num3 la $a2, num4 jal maxDouble li $v0, 4 la $a0, msg syscall li $v0, 3 syscall li $v0, 4 la $a0, newline syscall li $v0, 10 syscall maxFloat: l.s $f0, ($a0) l.s $f2, ($a2) mov.s $f12, $f2 c.lt.s $f0, $f2 bc1t skipF mov.s $f12, $f0 skipF: jr $ra maxDouble: l.d $f0, ($a0) l.d $f2, ($a2) mov.d $f12, $f0 c.lt.d $f0, $f2 bc1t skipD jr $ra skipD: mov.d $f12, $f2 jr $ra
内容的提问来源于stack exchange,提问作者randaz99
相关产品推荐
相关产品推荐

