tags:

views:

726

answers:

4

Let's say I have $t0, and I'd like to divide its integer contents by two, and store it in $t1.

My gut says: srl $t1, $t0, 2

... but wouldn't that be a problem if... say... the right-most bit was 1? Or does it all come out in the wash because the right-most bit (if positive) makes $t0 an odd number, which becomes even when divided?

Teach me, O wise ones...

+2  A: 

To do unsigned integer division, thats right. This only works for unsigned integers and if you don't care about the fractional part.

Daniel A. White
Cheers, buddy. Was getting a little worried I was missing something...
Julian H. Lam
You did miss something. See Tom's comment above -- you shift by **1** to divide by two, not two bits!
Andy Ross
+2  A: 

You will want to use a shift amount of 1, not 2:

srl $t1, $t0, 1

If you use 2, you will end up dividing by 4. In general, shifting right by x divides by 2x.

Greg Hewgill
Oh right, silly mistake on my part. I meant 1, not two, thanks for catching that one.
Julian H. Lam
+5  A: 

Use instruction sra: Shift right arithmetic !!

sra $t1, $t0, 1

Divides the content of $t0 by the first power of 2.

Description: Shifts a register value right by the shift amount (shamt) and places the value in the destination register. The sign bit is shifted in.

Operation: $d = $t >> h;

advance_pc (4);

Syntax: sra $d, $t, h

Encoding: 0000 00-- ---t tttt dddd dhhh hh00 0011

Why is this important? Check this simple program that divides an integer number (program's input) by 2.

    #include <stdio.h>

    /*
    * div divides by 2 using sra
    * udiv divides by 2 using srl
    */
    int div(int n);//implemented in mips assembly.
    int udiv(int n);
    int main(int argc,char** argv){

            if (argc==1) return 0;
            int a = atoi(argv[1]);

            printf("div:%d udiv:%d\n",div(a),udiv(a));
            return 1;
    }
    //file div.S
    #include <mips/regdef.h>

    //int div(int n)
    .globl div 
    .text
    .align 2
    .ent div
    div:
            sra v0,a0,1
            jr  ra        //Returns value in v0 register.
    .end div

    //int udiv(int n)
    .globl udiv
    .text
    .align 2
    .ent udiv
   udiv:
     srl v0,a0,1
     jr  ra        //Returns value in v0 register.
   .end udiv

Compile

root@:/tmp#gcc -c div.S
root@:/tmp#gcc -c main.c
root@:/tmp#gcc div.0 main.o -o test

Test drives:

root@:~# ./test 2
div:1 udiv:1
root@:~# ./test 4
div:2 udiv:2
root@:~# ./test 8
div:4 udiv:4
root@:~# ./test 16
div:8 udiv:8
root@:~# ./test -2
div:-1 udiv:2147483647
root@:~# ./test -4
div:-2 udiv:2147483646
root@:~# ./test -8
div:-4 udiv:2147483644
root@:~# ./test -16
div:-8 udiv:2147483640
root@:~#

See what happens? The srl instruction is shifting the sign bit

-2 = 0xfffffffe

if we shift one bit to the right, we get 0x7fffffff

0x7ffffffff = 2147483647

Of course this is not a problem when the number is a positive integer, because the sign bit is 0.

Tom
sra over srl? I'll take a look, thanks!
Julian H. Lam
I added an example, running on gxemul mips
Tom
A: 

If you are concerned about "rounding" and you want to round up, you can just increment by 1 before doing the logical (unsigned) shift.

And other have stated it previously but you only shift by 1 to divide by 2. A right shift by N bits divides by 2^N.

To use rounding (rounding up at 0.5 or greater) with shift values of N other than 1, just add 1<<(N-1) prior to the shift.

Adisak