Home > OS >  Divide an integer by the power of two in C
Divide an integer by the power of two in C

Time:06-14

When try to divide an integer by the power of two in two different ways, I get two different output. One way is to right shift it by k bits. Another way is to divide it by (1<<k).

#include <stdio.h>

int main(void) {
  int y1 = 0x00061290;
  int y2 = 0xFFFE1A32;
  printf("(y1/(1<<k))=%x\n", (y1/(1<<5)));
  printf("(y2/(1<<k))=%x\n", (y2/(1<<5)));
  printf("(y1>>k))=%x\n", (y1>>5));
  printf("(y2>>k))=%x\n", (y2>>5));
  return 0;
}

Output:

(y1/(1<<k))=3094
(y2/(1<<k))=fffff0d2
(y1>>k))=3094
(y2>>k))=fffff0d1

When y1 is 0x00061290, the outputs of that integer are the same. When y2 is 0xFFFE1A32, the outputs of that integer are different.

I guess it is because y1 is positive and y2 is not. But I am not sure. Can someone tell me under what situation they are different and what situation they are the same? And explain the difference of two operations if possible. Thanks

CodePudding user response:

The reason you get different output is y1 and y2 are defined as int instead of unsigned int. Signed division rounds toward 0 (as specified since C99), whereas shifting a negative value to the right rounds toward negative infinity on your platform. The C Standard specifies that the behavior for shifting negative values to the right is implementation defined.

Note however that passing an int to printf for a %x format has undefined behavior.

If you defined y1 and y2 as unsigned, the division and the shift will produce the same result.

Here is a modified version:

#include <stdio.h>

int main() {
    unsigned y1 = 0x00061290;
    unsigned y2 = 0xFFFE1A32;
    printf("y1=%x, y1/(1<<k)=%x\n", y1, y1 / (1<<5));
    printf("y2=%x, y2/(1<<k)=%x\n", y2, y2 / (1<<5));
    printf("y1=%x, y1>>k=%x\n", y1, y1 >> 5);
    printf("y2=%x, y2>>k=%x\n", y2, y2 >> 5);
    printf("\n");
    int i1 = 511;
    int i2 = -511;
    printf("i1=%d, i1/(1<<k)=%d\n", i1, i1 / (1<<5));
    printf("i2=%d, i2/(1<<k)=%d\n", i2, i2 / (1<<5));
    printf("i1=%d, i1>>k=%d\n", i1, i1 >> 5);
    printf("i2=%d, i2>>k=%d\n", i2, i2 >> 5);
    return 0;
}

Output:

y1=61290, y1/(1<<k)=3094
y2=fffe1a32, y2/(1<<k)=7fff0d1
y1=61290, y1>>k=3094
y2=fffe1a32, y2>>k=7fff0d1

i1=511, i1/(1<<k)=15
i2=-511, i2/(1<<k)=-15
i1=511, i1>>k=15
i2=-511, i2>>k=-16

CodePudding user response:

In general, and in lots of assembly languages, there's 2 different "right shift" operations - a bitwise shift right (which shifts literal bits without treating the sign any different) and a logical shift right which "smears" the sign bit into the left as old bits are removed from the right so that the sign is preserved and it behaves like (repeated) division by 2.

The designers of C couldn't be bothered having different operators for the different cases (e.g. perhaps a ">>" for bitwise shift right and ">>>" for logical shift right); and couldn't be bothered defining the actual behavior of their "shift right" so that people writing portable code can write fast code (e.g. if the CPU supports bitwise shift right and the programmer wants bitwise shift right, then a programmer writing portable code can't expect >> to give them a bitwise shift right).

Instead; the C designers made a right shift of a signed integer "implementation defined" so that it's essentially useless.

Your specific compiler is treating >> as a bitwise right shift (and not as a logical right shift). This causes the different behavior because the sign isn't preserved.

A different compiler (even on the same CPU) might use a logical shift and might give the same result.

The other problem is that int y2 = 0xFFFE1A32; is trying to put a "too big positive number" (larger than INT_MAX) into an int, and the compiler doesn't warn you about it and just pretends that your positive number is a negative number. This is caused by unrelated misguided foolishness in the C specification - specifically, integer overflow is defined as wrapping/truncation (and therefore trying to shove an elephant into a shoe box is considered perfectly valid and not a trivially detected bug).

  •  Tags:  
  • c
  • Related