2

I'm trying to rotate a signed char (*c), which in binary is 10011010, right 4 (numRotate) places. The desired outcome after the shift is 10101001. The code I currently have is:

void right(char *c, int numRotate) {
    *c = (*c >> numRotate) | (*c << (8 - numRotate));
}

According to what I've learnt, this apparently should work to do my desired shift correctly. Instead the outcome I have been getting is 11111001. I'm not sure what's wrong. Could it be a problem with signed vs unsigned char data types? All the resources I've looked at only use unsigned data types.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Moca
  • 43
  • 2
  • 7
  • the fact of data type being signed or unsigned has no relation to the bit order. well, unless you do arithmetic operations on it, like subtraction above. bitwise operations like shifts do not take a sign into account – mangusta Oct 20 '18 at 05:31
  • 1
    Convert the `*c` to `unsigned char` before shifting. Your compiler does an arithmetic shift right on signed types, and plain `char` appears to be a signed type on your machine. (`*c = ((unsigned char)(*c) >> numRotate) | ((unsigned char)(*c) << (8 - numRotate));`) – Jonathan Leffler Oct 20 '18 at 05:44
  • @mangusta It's indeed related to the fact that `*c` is signed. Shift right on signed variable is implementation defined. Cast it to `unsigned char` before the shifts. – Alex Lop. Oct 20 '18 at 05:46
  • 2
    Possible duplicate of [Arithmetic bit-shift on a signed integer](https://stackoverflow.com/questions/4009885/arithmetic-bit-shift-on-a-signed-integer) – Dluzak Oct 20 '18 at 06:01
  • @AlexLop do you mean implementation of C standard library? i thought if we shift say 11111111 (a byte-sized data type) to left once, we get 11111110, no matter what the sign is. The OP complains about bit pattern she gets, not its numeric value – mangusta Oct 20 '18 at 08:05
  • @mangusta I was talking about shift right, not left. – Alex Lop. Oct 20 '18 at 08:33
  • @Moca: you can accept one of the answers by clicking on the grey checkmark below its score. – chqrlie Oct 21 '18 at 15:15

2 Answers2

0

It has been answered in other post Arithmetic bit-shift on a signed integer. To get the result you expect you should use unsigned char.

void right(unsigned char *c, int numRotate) {
    *c = (*c >> numRotate) | (*c << (8 - numRotate));
}

With signed integer right shift operator will pad the remaining space with the sign bit (MSB) so:

10011010 >> 4 == 11111001
01011010 >> 4 == 00000101

The answer from the question I linked states it is compiler/platform specific. It is so to make common optimization technique of replacing multiplication/division by powers of 2 with bitshift operations work with negative numbers. Example:

#include <stdio.h>

int main()
{
    char x = -4;
    printf("0x%x -> %i\n", x, x);

    x <<= 1; //multiply by 2^1 = 2
    printf("0x%x -> %i\n", x, x);

    x >>= 2; //divide by 2^2 =4
    printf("0x%x -> %i\n", x, x);

    return 0;
}

Output:

0xfffffffc -> -4
0xfffffff8 -> -8
0xfffffffe -> -2
Dluzak
  • 315
  • 1
  • 11
0

Right shifting a negative value has implementation defined behavior. The pattern 10011010 has a negative value if char is 8-bit wide and signed by default on your platform. You must use unsigned char to have defined behavior for your purpose:

void right(char *c, int numRotate) {
    *c = ((unsigned char)*c >> numRotate) | ((unsigned char)*c << (8 - numRotate));
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189