MIPS中的整数绝对值?

11

你有什么简单的方法使MIPS寄存器中的值成为绝对值吗?

5个回答

26
这里是一个无分支的变体:
# input and output in $t0
sra $t1,$t0,31   
xor $t0,$t0,$t1   
sub $t0,$t0,$t1    

这是如何工作的?
首先,将$t1填充为$t0的符号位。因此,如果$t0为正数,则$t1将设置为0;如果$t0为负数,则$t1将设置为0xFFFFFFFF。

接下来,如果$t1为0xFFFFFFFF,则反转$t0的每个位,否则保持不变。恰好反转一个数的所有位等同于将它设置为(-number)-1(在二进制补码中)。

最后,从中间结果中减去0xFFFFFFFF(相当于-1)或0。
因此,如果$t0最初为负数,则会得到:
$t0 = ($t0 ^ 0xFFFFFFFF) - 0xFFFFFFFF == (-$t0 - 1) - -1 == (-$t0 - 1) + 1 == -$t0
如果最初为正数,则会得到:
$t0 = ($t0 ^ 0) - 0 == $t0

6
警告:该方法受到美国专利#6073150的保护。虽然已经早在1997年之前就有人知道了,但可能是无效的。 - Myria
1
@Myria:编译器(如GCC)多年来一直在各种ISA上使用这种技术。例如,x86-64的GCC4.4:https://godbolt.org/z/Mv843o。(还显示了64位MIPS GCC5.4使用条件移动movn来在x-x之间进行选择,就像clang为x86所做的那样。) - Peter Cordes

14

这里有一个非常简单的方法来实现它。

#assume you want the absolute value of r1
        ori $2, $zero, $1      #copy r1 into r2
        slt $3, $1, $zero      #is value < 0 ?
        beq $3, $zero, foobar  #if r1 is positive, skip next inst
        sub $2, $zero, $1      #r2 = 0 - r1
foobar:
#r2 now contains the absolute value of r1

请注意,这是针对没有分支延迟槽的MIPS处理器的编程,例如默认情况下由MARS / SPIM模拟。否则,您需要将“move”(“ori”)重新排列到分支延迟槽中。 - Peter Cordes

10
最简单的方式。 有一个伪指令可以实现这个功能:
abs $t1, $t1

将寄存器$t1中的值取绝对值,并将其放入$t1中。


3
这个伪指令指的是另一个答案中展示的sra/xor/sub序列。 - Myria

2
这是一个经过大小优化的版本。由于分支预测问题,它比sra/xor/subu答案慢,但指令少了一个:
    bgtz $t0, label
label:
    subu $t0, $zero, $t0

这是由于MIPS延迟槽的原因:如果$t0为正数,则否定$t0subu指令将执行两次。您可能需要在汇编程序中启用.set noreorder

1

最简单的方法就是对这些值进行一些二进制数学运算。

http://en.wikipedia.org/wiki/Signed_number_representations 描述了各种系统如何存储它们的负数。我相信MIPS使用“二进制补码”方案来存储有符号数。这使得它比位标志更难处理,但仍然可以做到。


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