MIPS中addiu操作溢出检查及斐波那契数列溢出终止实现问询
解决MIPS斐波那契数的溢出检查问题
先看你当前的代码,这里有两个核心问题:一是加法操作数搞错了(你用了索引值$t2来做加法,而不是前一个斐波那契数),二是用了addu无符号加法——它不会触发溢出标志,没法直接检测有符号数的溢出。我们一步步来修正:
第一步:修正加法逻辑与指令
首先,循环里要确保$t0和$t1始终是前两个斐波那契数,然后用add指令(而非addu)来计算下一个数——add会在有符号数溢出时设置CPU的溢出标志位,这是我们检测溢出的关键:
loop: # $t0 = Fib(n-2), $t1 = Fib(n-1) add $t3, $t0, $t1 # 计算Fib(n),用add触发溢出标志
第二步:检测溢出
MIPS里有两种常用的溢出检测方式:
方式1:用专用溢出分支指令
直接用bov(Branch on Overflow)指令,一旦add触发溢出就跳转到退出逻辑:
bov overflow_exit # 溢出则跳转到收尾流程
方式2:手动判断符号位(兼容部分汇编器)
如果你的环境不支持bov,可以通过符号位异或来判断:两个同号数相加,结果和原数异号就说明溢出:
xor $t4, $t0, $t1 bgez $t4, no_overflow # 两个数异号,相加不可能溢出 xor $t4, $t0, $t3 bgtz $t4, overflow_exit # 结果与原数异号,溢出 no_overflow:
第三步:存储有效结果并更新状态
如果没有溢出,就把结果存入数组,然后更新循环所需的变量:
# 存储当前斐波那契数到数组 sw $t3, 8($s0) # 第一次循环存在偏移8的位置,后续用寄存器跟踪偏移更稳妥 # 更新前两个数,为下一次循环做准备 move $t0, $t1 move $t1, $t3 # 更新计数(已计算的斐波那契数总数) addi $t2, $t2, 1 # 更新数组偏移(建议用单独寄存器$t4跟踪,避免重复计算) addi $t4, $t4, 4 j loop
第四步:溢出时的收尾工作
当溢出发生时,把最终的有效斐波那契数个数存入NUM_FBN,然后退出程序:
overflow_exit: sw $t2, NUM_FBN # 存储有效斐波那契数的总数 # MIPS退出系统调用 li $v0, 10 syscall
完整修正后的代码片段
整合所有逻辑后的完整代码如下:
.data FBN: .space 400 # 最多存储100个4字节的斐波那契数 .align 2 NUM_FBN: .space 4 # 存储有效斐波那契数的个数 .align 2 .text .globl main main: la $s0, FBN li $t0, 0 # Fib(0) li $t1, 1 # Fib(1) sw $t0, 0($s0) sw $t1, 4($s0) li $t2, 2 # 已存储2个有效数(Fib(0)和Fib(1)) li $t4, 8 # 下一个存储位置的偏移量 loop: add $t3, $t0, $t1 # 计算下一个斐波那契数 bov overflow_exit # 检测溢出,溢出则退出 # 存储结果到数组 sw $t3, ($t4)($s0) # 更新前两个数 move $t0, $t1 move $t1, $t3 # 更新计数和偏移 addi $t2, $t2, 1 addi $t4, $t4, 4 # 额外检查数组是否存满(可选) ble $t4, 396, loop # 400字节的最后一个有效位置是396($s0) overflow_exit: sw $t2, NUM_FBN li $v0, 10 syscall
关键注意点
- 用
add而非addu:addu是无符号加法,即使溢出也不会设置溢出标志,不适合斐波那契数这类有符号整数的计算。 - 计数初始值:初始已存储Fib(0)和Fib(1),所以计数
$t2要从2开始,这样最终NUM_FBN的值才是有效数的总数。 - 数组偏移跟踪:用单独的寄存器
$t4跟踪存储位置,避免每次手动计算偏移出错。
内容的提问来源于stack exchange,提问作者Ars




