Do you have any simple ways to make a value in a register in MIPS as an absolute value?
Here's a branch-less variant:
# input and output in $t0
sra $t1,$t0,31
xor $t0,$t0,$t1
sub $t0,$t0,$t1
How does this work?
First, $t1
is filled with the sign-bit of $t0
. So if $t0
is positive $t1
will be set to 0, and if $t0
is negative $t1
will be set to 0xFFFFFFFF.
Next, each bit of $t0
is inverted if $t1
is 0xFFFFFFFF, or left unchanged if $t1
is 0. It just so happens that inverting all bits of a number is the same as setting it to (-number)-1
(in two's complement).
Finally, either 0xFFFFFFFF (which equals -1) or 0 is subtracted from the intermediate result.
So if $t0
originally was negative you'll get:$t0 = ($t0 ^ 0xFFFFFFFF) - 0xFFFFFFFF
== (-$t0 - 1) - -1
== (-$t0 - 1) + 1
== -$t0
.
And if it originally was positive you'll get:$t0 = ($t0 ^ 0) - 0
== $t0
.
Here is a pretty simple way to do it.
#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
Here's a size-optimized version of it. It's slower than the sra/xor/subu answer, due to branch prediction issues, but it's one instruction smaller:
bgtz $t0, label
label:
subu $t0, $zero, $t0
This works because of the MIPS delay slot: if $t0
is positive, the subu
instruction to negate $t0
executes twice. You may need to enable .set noreorder
in your assembler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With