MIPS汇编二分法求平方根程序异常问题求助
二分法求平方根的MIPS汇编循环异常问题
我在将一段二分法计算平方根的C程序转换为MIPS汇编代码时遇到了问题:程序的while循环比预期少执行一次,比如计算sqrt(16)时返回8,计算sqrt(4)时返回4。我尝试过调整栈中结果的存储位置,但没有解决问题,目前代码回到初始状态,使用QtSpim编译运行。
原C代码
#include <stdio.h> float fsqrt(float x) { if (x == 0) return 0; float xhi = x; float xlo = 0; float guess = x / 2; float error = 0; error = guess * guess - x; if (error < 0) { error = 0 - error; } while (error > 0.00001) { if (guess * guess > x) { xhi = guess; } else { xlo = guess; } guess = (xhi + xlo) / 2; error = guess * guess - x; if (error < 0) { error = 0 - error; } } return guess; } int main() { float number; printf("Enter a non-negative number: "); scanf("%f", &number); printf("Square root is %.6f\n", fsqrt(number)); return 0; }
当前MIPS代码
.data float0: .float 0.0 # For comparing floats to 0 errorCheck: .float 0.00001 # Error margin for correct sqrt enterText: .asciiz "Enter a non-negative number: " outputText: .asciiz "Square root is " newLine: .asciiz "\n" .text main: # Request number li $v0, 4 la $a0, enterText syscall # Read number (float) li $v0, 6 syscall s.s $f0, 0($sp) # Save number to 0 on stack # Calculate square root jal fsqrt # Print square root text li $v0, 4 la $a0, outputText syscall # Print square root number li $v0, 2 l.s $f12, 4($sp) syscall # Print new line li $v0, 4 la $a0, newLine syscall # Exit program li $v0, 10 syscall fsqrt: l.s $f0, 0($sp) # Load input number into $f0(x) from stack l.s $f31, float0 # Load float0 into $f31 li.s $f29, 2.0 # Load 2.0 into $f29 for divisor l.s $f28, errorCheck # Load error check into $f28 for checking error # Increase stack for output/return (Return will be saved to 4($sp)) addi $sp, $sp, -4 # Return 0 if input == 0 c.eq.s $f0, $f31 bc1t return0 mov.s $f1, $f0 # Load x into "xhi" mov.s $f2, $f31 # Load 0 into "xlo" div.s $f3, $f0, $f29 # Load x/2 into "guess" mov.s $f4, $f31 # Load 0 into "error" mul.s $f30, $f3, $f3 # Load guess^2 into $f30 temporarily sub.s $f4, $f30, $f0 # error = guess^2 - x # Error = -Error if less than 0 c.lt.s $f4, $f31 bc1t errorLTZero j while while: # If error > 0.00001, end loop (exit function) c.le.s $f4, $f28 bc1f exitFunc mul.s $f30, $f3, $f3 # Loads guess^2 into $f30 for conditional # if guess^2 < x, xlo = guess c.lt.s $f30, $f0 bc1t guessSqLTx # else xhi = guess mov.s $f1, $f3 add.s $f30, $f1, $f2 # Load "xhi + xlo" into temp float register div.s $f3, $f30, $f29 # Load "xhi + xlo / 2" into guess mul.s $f30, $f3, $f3 # Load guess^2 into $f30 temporarily sub.s $f4, $f30, $f0 # Load "guess - x" into $f30 temporarily+ # Error = -Error if less than 0 c.lt.s $f4, $f31 bc1t errorLTZero s.s $f3, 4($sp) # Save guess to stack j while j exitFunc return0: s.s $f31, 4($sp) jr $ra errorLTZero: sub.s $f4, $f31, $f4 jr $ra exitFunc: s.s $f3, 4($sp) jr $ra guessSqLTx: mov.s $f2, $f3 jr $ra
问题分析与修复方案
核心问题1:循环条件判断逻辑颠倒
在MIPS的while标签处,判断逻辑完全反转:
# If error > 0.00001, end loop (exit function) c.le.s $f4, $f28 bc1f exitFunc
c.le.s $f4, $f28是判断error <= 0.00001,bc1f exitFunc表示条件不成立(即error > 0.00001)时退出循环,这和原C代码的while (error > 0.00001)逻辑完全相反,导致第一次循环直接退出,没有执行迭代。
修复:
while: # 如果 error <= 0.00001,退出循环 c.le.s $f4, $f28 bc1t exitFunc
使用bc1t(条件成立时跳转),当误差符合要求时才退出循环,否则继续迭代。
核心问题2:分支处理后错误返回主程序
errorLTZero和guessSqLTx分支使用jr $ra返回,而$ra是fsqrt的调用返回地址,直接跳回会中断循环。正确做法是处理完后跳回循环流程,而非返回主程序。
修复:
- 拆分初始误差处理和循环内误差处理的分支:
# 初始误差处理 c.lt.s $f4, $f31 bc1t errorLTZero_init j while errorLTZero_init: sub.s $f4, $f31, $f4 j while
- 调整循环内分支的跳转逻辑,避免直接返回:
mul.s $f30, $f3, $f3 # Loads guess^2 into $f30 for conditional # if guess^2 < x, xlo = guess c.lt.s $f30, $f0 bc1t guessSqLTx # else xhi = guess mov.s $f1, $f3 j update_guess guessSqLTx: mov.s $f2, $f3 update_guess: add.s $f30, $f1, $f2 # Load "xhi + xlo" into temp float register div.s $f3, $f30, $f29 # Load "xhi + xlo / 2" into guess mul.s $f30, $f3, $f3 # Load guess^2 into $f30 temporarily sub.s $f4, $f30, $f0 # Load "guess^2 - x" into error # Error = -Error if less than 0 c.lt.s $f4, $f31 bc1t errorLTZero_loop s.s $f3, 4($sp) # Save guess to stack j while errorLTZero_loop: sub.s $f4, $f31, $f4 j while
其他优化点
- 避免使用
$f31:$f31是浮点返回寄存器,改用其他空闲寄存器(如$f20)存储常量,避免冲突。 - 栈操作保持一致性:确保返回值存储位置不会覆盖输入数据。
内容的提问来源于stack exchange,提问作者Chaserix
相关产品推荐
相关产品推荐

