11

Do you have any simple ways to make a value in a register in MIPS as an absolute value?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
aherlambang
  • 14,290
  • 50
  • 150
  • 253

5 Answers5

26

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.

Michael
  • 57,169
  • 9
  • 80
  • 125
  • 6
    Warning: This method is covered by U.S. patent #6073150. Probably invalid as hell, though, since it's been known longer than 1997. – Myria Aug 08 '15 at 00:47
  • 1
    @Myria: Compilers such as GCC have been using this for years on various ISAs. e.g. GCC4.4 for x86-64: https://godbolt.org/z/Mv843o. (Also shows 64-bit MIPS GCC5.4 using conditional move, `movn`, to select between `x` and `-x` like clang does for x86.) – Peter Cordes Oct 26 '20 at 00:44
14

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
swanson
  • 7,377
  • 4
  • 32
  • 34
  • Note that this is for a MIPS *without* branch-delay slots, like MARS / SPIM simulate by default. Otherwise you'd want to rearrange the `move` (`ori`) into the branch-delay slot. – Peter Cordes Oct 26 '20 at 00:47
10

Simplest way of all. There is a pseudo instruction that does this:

abs $t1, $t1

will take the absolute value of the value in register $t1 and place it in $t1

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
buster
  • 101
  • 1
  • 2
2

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.

Myria
  • 3,372
  • 1
  • 24
  • 42
1

The easiest way would just to do a bit of binary math on the values.

http://en.wikipedia.org/wiki/Signed_number_representations describes how various systems store their negative numbers. I believe MIPS uses a two's complement scheme to store signed numbers. This makes it a bit harder than a bit flag, which could just be turned off by ANDing the number with 0b01111111, but it is still doable.

David Pfeffer
  • 38,869
  • 30
  • 127
  • 202