assembly 在MIPS程序集上求整数的平方根

ccrfmcuu  于 2023-10-19  发布在  其他
关注(0)|答案(8)|浏览(114)

我怎么能找到一个整数的平方根使用MIPS汇编?

8mmmxcuj

8mmmxcuj1#

我们可以使用像提交的for this question这样的算法,并根据需要进行调整。在进入MIPS之前,让我们看看C中的实现:

//Function to compute sqroot(n)
int sqroot(int n) {
        int x = n;
        for (int i = 0; i < (n/2); i++)
             x = (x + n / x) / 2;

        return x;
}

sqroot(n)函数将计算与n的平方根的下限相等的整数。所以如果你调用sqroot(225),你会得到15,但是sqroot(15)会返回3而不是3.87298。
从C代码中,我们可以概述MIPS代码的外观:

In calling function:
    Load the number to be squared into $a0
    jal root

root:
    Initialize $t0 = n, $t1 = i = 0, $t2 = x = n = $a0, $t3 = n/2

Loop:
    Divide n/x
    Add x to n/x
    Divide (x + n/x) by 2
    Check if $t1 < $t3
    If it is, branch back to loop
    Else, move x into return register $v0

请注意:
1.确保根据需要推送和弹出堆栈。我为了简单起见把它省略了。
1.当除以2的幂时,可以使用srl指令。
1.有关MIPS指令的说明和其他信息,请参阅click here

huwehgph

huwehgph2#

我发现牛顿的方法x = (x + n/x) / 2在只处理整数时不能令人满意,因为终止条件很难精确计算。n/2只是一个猜测,并且几乎总是比必要的迭代次数更多。牛顿的方法是 * 二次收敛 *,并且不是正比于n,而是sqrt(n)。另一个建议,“不断重复直到x停止变化”也不起作用,因为对于非完美正方形x将在根的下限和上限之间交替-因为整数数学,当x略小于或略大于sqrt(n)时,项n/x将交替。
我从wikipedia上取了一个逐位根计算方法,并创建了一个MIPS版本。它不会受到低效率(n/2)或模糊性(floor(sqrt(n))ceil(sqrt(n)))的影响。可扩展的方法可以更有效地返回结果,但假设查找表不可用,这是一个很好的可靠方法。
首先,我将C示例转换为仅使用小于(<)比较,因为MIPS仅提供设置小于slt比较指令。

int isqrt(int num) {
  int ret = 0;
  int bit = 1 << 30; // The second-to-top bit is set

  // "bit" starts at the highest power of four <= the argument.
  while (num < bit) {
    bit >>= 2;
  }

  while (bit != 0) {
    if (num < ret + bit) {
      ret >>= 1;
    } else {
      num -= ret + bit;
      ret = (ret >> 1) + bit;
    }
    bit >>= 2;
  }
  return ret;
}

以下是生成的MIPS代码:

isqrt:
  # v0 - return / root
  # t0 - bit
  # t1 - num
  # t2,t3 - temps
  move  $v0, $zero        # initalize return
  move  $t1, $a0          # move a0 to t1

  addi  $t0, $zero, 1
  sll   $t0, $t0, 30      # shift to second-to-top bit

isqrt_bit:
  slt   $t2, $t1, $t0     # num < bit
  beq   $t2, $zero, isqrt_loop

  srl   $t0, $t0, 2       # bit >> 2
  j     isqrt_bit

isqrt_loop:
  beq   $t0, $zero, isqrt_return

  add   $t3, $v0, $t0     # t3 = return + bit
  slt   $t2, $t1, $t3
  beq   $t2, $zero, isqrt_else

  srl   $v0, $v0, 1       # return >> 1
  j     isqrt_loop_end

isqrt_else:
  sub   $t1, $t1, $t3     # num -= return + bit
  srl   $v0, $v0, 1       # return >> 1
  add   $v0, $v0, $t0     # return + bit

isqrt_loop_end:
  srl   $t0, $t0, 2       # bit >> 2
  j     isqrt_loop

isqrt_return:
  jr  $ra

您可以像其他MIPS过程一样调用它:

addi  $a0, $zero, 15
jal   isqrt # v0 = result

此过程始终为 * 正参数 * 返回$v0 = floor(sqrt($a0))

注意:代码会因为 * 负数参数 * 而进入无限循环。在调用此过程之前对输入进行清理。

js5cn81o

js5cn81o3#

它不是在MIPS中,但仍然是组装。我发现的基本算法是基于这样一个事实,即前n个奇数加在一起= n^2。
所以如果你利用这一点,通过颠倒这个过程,从你想要的数字中减去平方根,你可以循环得到精确的答案,或者近似值。我相信它的根+1非完美的平方。
这个想法是,你循环的次数是n,这是你的平方根。
希望这对你有帮助。

mov eax, 9513135         ; eax = number to take square root of
    mov ebx, eax            ; make a copy of eax in ebx

    loopIt :
        sub ebx, count      ; count starts as 1, 3, 5, 7, 9
        inc count           ; count = even
        inc count           ; count = odd
        inc sqrt            ; gives sqrt value
        mov eax, sqrt
        cmp ebx, 0
        js timetoReturn     ; return value if signed num, aka goes over zero
        jnz loopIt

    timetoReturn :
        mov reg, eax            ; just outputting the value
zbdgwd5y

zbdgwd5y4#

你可以尝试这个算法,它给出的整数小于或等于你的数字的平方根。
假设你想要n的平方根。然后重复以下计算:
x = (x + n/x) / 2
选择x = n开始,并不断重复,直到x停止变化。

hjzp0vay

hjzp0vay5#

这里有一个简单易懂的算法,用于计算正整数平方根的下限,在C中:

int approx_sqrt(int x) {
    int result;
    for (int partialSum = 0, oddNum = 1; partialSum < x; partialSum += oddNum, oddNum +=2) result++;
    return result;
}

它与okstory的答案基于相同的原则,只是方式略有不同。
理论:递增的奇数被添加到partialSum,只要partialSum小于操作数。该结果等于奇数的数量相加以产生partialSum。

xoefb8l8

xoefb8l86#

你们都错了
你可以使用sqrt.s或sqrt.d汇编代码!(ex)平方米$12,$13
不要浪费你的时间来实现这些功能。

vmdwslir

vmdwslir7#

如果你想计算一个整数的平方根,你首先需要将整数转换为浮点数。假设你想取平方根的数字存储在$t1中,那么它到浮点数的转换如下所示

mtc1 $t1, $f1
cvt.s.w $f1, $f1

现在您可以使用sqrt.s函数计算平方根。

sqrt.s $f1,$f1

所以现在$f1将保存存储在$t1中的整数的平方根

zqdjd7g9

zqdjd7g98#

下面是MIPS 32中的一个过程/函数,用于查找整数的平方根。

SQUARE_ROOT:

    # res = n
    # while(i < n/2)
    # res = (res + n / res) / 2;
    
    addi $sp, $sp, -20
    sw   $ra, 16($sp)               # Saving the return address into stack
    sw   $s3, 12($sp)               # Saving other register values
    sw   $s2,  8($sp)
    sw   $s1,  4($sp)
    sw   $s0,  0($sp)
    
    add  $s3, $zero, $a0            # s3 = n
    add  $s0, $zero, $a0            # res = n
    
    addi $a1, $zero, 2              # A = N, B = 2
    jal DIVISON                     # Divison(n, 2)
    
    add $s1, $zero, $v0             # s1 = n / 2
    addi $s2, $zero, 0              # idx (s2) = 0
    
SQUARE_ROOT_LOOP:
    beq  $s1, $s2, EXIT_SQUARE_ROOT # if (idx == n/2) break
    add $a0, $s3, $zero             # A = n
    add $a1, $s0, $zero             # B = res
    jal DIVISON                     # Divison(n, res)
    
    add  $s0, $s0, $v0              # res = res + (n/res)
    add  $a0, $zero, $s0            # A = res + (n/res)
    addi $a1, $zero, 2              # B = 2
    
    jal DIVISON                     # Divison((res + (n / res), 2)
    add  $s0, $zero, $v0            # res = res + (n / res) / 2
    
    addi $s2, $s2, 1                # idx++
    j SQUARE_ROOT_LOOP
    
EXIT_SQUARE_ROOT:       
    lw $s0, 0($sp)                  # Retrieving data stored in stack
    lw $s1, 4($sp)
    lw $s2, 8($sp)
    lw $s3, 12($sp)
    lw $ra, 16($sp)                 # Retrieving Return Address
    
    addi $sp, $sp, 20               # Reseting stack pointer 
    jr $ra                              

DIVISON:
    # While (A >= B) A -= B; result++
    add  $sp, $sp, -12              # Allocating Stack Memory
    sw   $s2, 8($sp)                # Storing register values
    sw   $s1, 4($sp)                
    sw   $s0, 0($sp)
    
    addi $s0, $zero, 0              # result (s0) = 0
    add  $s1, $zero, $a0            # s1 = A
    add  $s2, $zero, $a1            # s2 = B
DIV_LOOP:
    blt  $s1, $s2, EXIT_DIV         # If (A < B) break
    sub  $s1, $s1, $s2              # A -= B
    addi $s0, $s0, 1                # result++
    j DIV_LOOP
EXIT_DIV:
    add $v0, $zero, $s0             # v0 = result
    
    lw   $s2, 8($sp)                # restoring register values
    lw   $s1, 4($sp)                
    lw   $s0, 0($sp)
    addi $sp, $sp, 12               # restoring stack pointer
    
    jr $ra

相关问题