0

Consider this C function;

int triangle(int width, int height)
{
    return (width * height) / 2;
}

When compiled with gcc (gcc -m32 -g -c test.c) produces following assembly (objdump -d -M intel -S test.o).

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <triangle>:

int triangle(int width, int height)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
    return (width * height) / 2;
   3:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
   6:   0f af 45 0c             imul   eax,DWORD PTR [ebp+0xc]
   a:   89 c2                   mov    edx,eax
   c:   c1 ea 1f                shr    edx,0x1f
   f:   01 d0                   add    eax,edx
  11:   d1 f8                   sar    eax,1
  13:   5d                      pop    ebp
  14:   c3                      ret    

I already know that shifting an integer n bit to right divides it by 2^n. However, according to above output, signed integers seems to be treated differently (which, of course, make sense). If I am reading assembly output correctly, sign bit of the integer added to itself before it is shifted.

What is the purpose of adding sign bit of the integer to itself before right shifting?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
yasar
  • 13,158
  • 28
  • 95
  • 160
  • 2
    It's to get correct result as per the C standard. `-3 / 2` is expected to be `-1` but `sar` makes it `-2` instead. Adding one to negative numbers fixes this problem. – Jester Sep 12 '18 at 18:52
  • 1
    [Which is better option to use for dividing an integer number by 2?](https://stackoverflow.com/q/10681375), [Dividing by power of 2 using bit shifting](https://stackoverflow.com/q/5061093), [Divide a signed integer by a power of 2](https://stackoverflow.com/q/39691817). – Peter Cordes Sep 12 '18 at 18:56
  • 1
    Supercat points out that arithmetic right shift (rounding towards -infinity) has useful properties for some applications ([Which is better option to use for dividing an integer number by 2?](https://stackoverflow.com/posts/comments/29953207)), vs. C signed division (`idiv`) semantics of rounding towards 0. – Peter Cordes Sep 12 '18 at 18:58

1 Answers1

2

It is to get the correct "rounding towards zero" result for negative numbers. Division by shifting rounds towards negative infinity, so negative numbers will have a different result compared to the expected result of the C division operator.

An example is -1: shifting right by 1 gives -1 still, but the C operator / 2 gives 0.

So the extra code is a correction for this effect. If you don't need that, use unsigned or an explicit shift (but the second option is less portable).

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386