如何在MIPS中找到数组的最小值

6

这是我的代码,我无法获得正确的输出。我做错了什么?我最初将min设置为0,然后检查数组是否小于或等于此值,如果是,则跳转到一个标签,并将min的值设置为数组的值,然后跳回到迭代数组。

xyz:    .word   -8, 16, -32, 64, -128, 256 

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2  
#   int total   $s3
#   
    .text
    .globl  main
main:
    la  $s0, xyz                # p = foo
    addi    $s1, $s0, 24        # end = p + 6
    add $s3, $zero, $zero       # total = 0 
    add     $s2, $zero, $zero   # min = 0
L1:
    beq $s0, $s1, L2    # if (p == end) goto L2
    lw  $t0, ($s0)      # $t0 = *p
    lw  $t1, ($s2)      # $t1 = min
    slt $t2, $t1, $t0       # check if min is less than p
    add $s3, $s3, $t0       # total += $t0
    bne     $t2, $zero, L3      # if min is less than p, go to L3 
    addi    $s0, $s0, 4     # p++
    j   L1
L2:     
    add $v0, $zero, $zero   # return value from main = 0
    jr  $ra

L3:
    move    $s2, $t0
    j   L1

你为什么要用汇编语言编程? - user4759923
你的标题说你想要数组的最小值,但是你的代码结构是为了获取最大值[还有一些错误]。你想要哪个值(最小值=-128或最大值=256)? - Craig Estey
哦,我正在尝试获取最小值。我做错了什么导致获取了最大值而不是最小值? - user3467226
我已经准备好了一个答案,很快就会发布,其中将注释这些内容[以及我发现的其他4个错误],并提供一个修复版本。然而,一个简单的slt不能很好地处理负数,因为它将数字视为无符号数。你需要处理负值吗? - Craig Estey
是的,否定数值对我来说非常重要,因为我需要学习如何在MIPS中处理它们。所以你的意思是slt将-128视为128吗? - user3467226
糟糕,我的错。slt是有符号版本[还有一个无符号的sltu]。我只是还没有得到预期的结果,所以请祈祷吧... - Craig Estey
1个回答

8

好的,我发现了几个bug。我已经创建了三个版本,并添加了输出系统调用,以便您可以查看结果[请原谅过度的样式清理]:


下面是带有错误注释的原始代码:

    .data
xyz:        .word       -8,16,-32,64,-128,256

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    # NOTE/BUG: to find minimum, you want to init this to the first array
    # element
    # also, initializing with a minimum value (e.g. 0), or more correctly, the
    # largest possible negative number (e.g. 0x80000000) implies a search for
    # maximum
    add     $s2,$zero,$zero         # min = 0

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2
    lw      $t0,($s0)               # $t0 = *p

    # NOTE/BUG: s2 is a register variable that contains "min" (e.g. int min)
    # and is _not_ a pointer to a "min" variable in memory (e.g. int *min)
    lw      $t1,($s2)               # $t1 = min

    # NOTE/BUG: the the check should be reversed:
    slt     $t2,$t1,$t0             # check if min is less than p
    add     $s3,$s3,$t0             # total += $t0

    bne     $t2,$zero,L3            # if min is less than p, go to L3

    # NOTE/BUG: this pointer increment is out of place (i.e. it does not
    # get incremented if there is a jump to L3)
    # this won't affect the min value, but it will double count the value in
    # the total
    addi    $s0,$s0,4               # p++
    j       L1

L2:
    add     $v0,$zero,$zero         # return value from main = 0
    jr      $ra

L3:
    move    $s2,$t0
    j       L1

这是一个修复后的版本:
    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$t0,$s2             # *p < min?
    bne     $t2,$zero,L3            # yes, fly

    j       L1

L2:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

L3:
    move    $s2,$t0                 # set new/better min value
    j       L1

这里是稍微清理过的版本,我反转了一个分支的意义并且能够缩短循环。此外,我更改了标签以便更加描述其角色和功能:

    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$s2,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    move    $s2,$t0                 # set new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

更新:

谢谢你的帮助,但是lw $t1,($s2)不能运行,因为lw只能在指针上工作。

没错。注意到你之前用s3来保存total,这就像代码中使用s2保存min一样。我这样做[部分原因]是由于顶部的注释:

#   int min     $s2

要使用lw,顶部的注释应该是:
#   int *min    $s2

为了在原有的方式下使用s2,你需要像这样做:
min:    .word    0

并且,在循环开始之前,您需要做以下操作:

la    $s2,min

而且,你需要对其进行lwsw,这会减慢速度。因此,除了已有的内容外,你需要添加额外的lwsw

mips有很多寄存器[它的优势]。因此,将自动的、函数作用域变量保存在其中可以加速程序运行。

然而,为了完整起见,这里提供允许使用lw的版本,就像你之前使用的那样。请注意额外的内存访问。它类似于使用-O0编译的C代码:

    .data
min:        .word       0
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int *min    $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    la      $s2,min                 # point to min
    lw      $t4,0($s0)              # fetch xyz[0]
    sw      $t4,0($s2)              # store in min

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    lw      $t4,0($s2)              # fetch min
    slt     $t2,$t4,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    sw      $t0,0($s2)              # store new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    lw      $a0,0($s2)              # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

谢谢,那帮了很大的忙。但是lw $t1,($s2)不起作用,因为lw只能在指针上工作? - user3467226

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接