Unexepected behavior from multiple bitwise shifts on the same line [duplicate] - c

This question already has answers here:
Why does combining two shifts of a uint8_t produce a different result?
(2 answers)
Closed 8 years ago.
I get a different result depending on if I combine multiple bitwise shifts on one line or put them on separate lines.
unsigned char a = 73;
a = (a << 6) >> 2;
printf("%d\n", a);
prints 144 when I expect 16
unsigned char a = 73;
a = a << 6;
a = a >> 2;
printf("%d\n", a);
prints the expected result of 16
when I shift left 6, then right 1 on one line, it prints the expected result of 32, so it
looks like the 2nd shift right is shifting in a 1 instead of a 0, but why?
Also
a = (unsigned char) (a << 6 ) >> 2;
produces the expected result (16).
Is the first left shift returning a signed char instead of an unsigned char, and if so why?

Yes, the result of the first shift is signed. signed int specifically. Check out the rules for the "integer promotions" in the C spec:
If an int can represent all values of the original type ... the value is converted to an int...
So, since your int - usually a range of [-231, 231) - can represent all the values of unsigned char - usually [0, 255] - it's converted to int, and the << and >> take place on the resulting values.

Related

Bit Operation in C

I am new in C and this is for a school project. I am implementing the Skinny Block Cipher in C.
My code:
unsigned char *bits[8]; // this array holds 1 byte of data.
... call in another func to convert hex to bit.
unsigned int four = bits[4] - '0'; // value 0
unsigned int seven = bits[7] - '0'; // value 1
unsigned int six = bits[6] - '0'; // value 1
four = four ^ ~(seven | six); // eq 1;
Now, my question
Do I have to convert the char to int every time to run the bit operation? What will happen if I do it using unsigned char?
If I store the value for eq - 1 on an unsigned int, the value is fe which is wrong (according to an online bit calculator), on the other hand, if I store the result in an unsigned char, the value is -2 which is correct. What's the difference? I am kind of lost here.
bits[8] is a pointer and I tried to do the eq 1 using indexes from bits pointer, like bits[4], etc but my VSCode throws an error and I don't understand why. Obviously, I have some gaps in my knowledge. I am using my Python knowledge to go through this.
I don't know if I am giving all the information that's needed. Hit me up for extras!
TIA.
I updated the code
unsigned char bits[9];
It converts a3 into 010100011.
unsigned char *bits[8]; // this array holds 1 byte of data.
No, it is an array of 8 pointers to char.
unsigned int four = bits[4] - '0'; // value 0
This will not work as you subtract the integer '0' from the pointer.
If you want to keep the string representation of the number in the binary form you need to define an array of 9 chars
char bits[9] = "10010110";
Then you can do the operations as in your code.
Do I have to convert the char to int every time to run the bit
operation? What will happen if I do it using unsigned char?
If you want to keep it as a string then - yes.
unsigned char x = 0x96;
unsigned int four = !!(x & (1 << 4));
unsigned int seven = !!(x & (1 << 7));
unsigned int six = !!(x & (1 << 6));

Different situations when shift count>= width of type [duplicate]

This question already has answers here:
Shifting a 32 bit integer by 32 bits
(2 answers)
Closed 7 years ago.
When shift count < width of type, it works as expected:
int a = 1 << (8 * sizeof(int) - 1);
printf("%x\n", a); // 80000000
When shift count >= width of type, the result is different every time and there seems no law:
int b = 1 << 8 * sizeof(int); // int b = 1 << (8 * sizeof(int) + 1);
printf("%x\n", b); // such as 59fa2ba8, 5b0f6ba8, 52f46ba8 etc
Well, there is a warning saying warning: shift count >= width of type [-Wshift-count-overflow]. If this can explain the random outcome, then let me put the shift count into a variable:
int k = 8 * sizeof(int);
int c = 1 << k;
int d = 1 << (k + 1);
printf("%x\n", c); // 1
printf("%x\n", d); // 2
The result is totally different from before, this is Circular shift! How to explain this?
int b = 1 << 8 * sizeof(int);
is subject to undefined behavior.
From the C99 Standard:
6.5.7 Bitwise shift operators
3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
It's pointless to try to make sense of the outcome of such an operation.

What is the point of bitwise and-ing with 255 when using bitshifting method of bytes reordering?

There is an article, explaining little/big-endian topic, and it contains an example of endianness conversion function:
short reverseShort (short s) {
unsigned char c1, c2;
if (is_bigendian()) {
return s;
} else {
c1 = s & 255;
c2 = (s >> 8) & 255;
return (c1 << 8) + c2;
}
}
I understand, why they and-ing here:
c1 = s & 255;
255 = 11111111, so bitwise and with s which may be like 1011011010010010, will not affect numbers in last 8 places, but turn all leading places to zeroes. So it's cuts bits to ending 8.
I also understand, why they using right shift here:
c2 = (s >> 8) & 255;
Shift by 8 cuts last 8 bits, so leading 8 remains. So in the first operation they get last 8 bits, and then first 8 bits.
But why they 'and'-ing it with 255 again?
lets say s = binary representation: abcdefghijklmnopqrst
c1 = s & 255; // < mnopqrst first 8 bits
c2 = (s >> 8) & 255; // from abcdefghijklmnopqrst >> 8 = abcdefghijkl & b11111111 = efghijkl
return (c1 << 8) + c2; // < (mnopqrst00000000) + efghijkl = mnopqrstefghijkl
its essentially taking the first 8 bits, and the next 8, then reverses them
But why they 'and'-ing it with 255 again?
Thats to truncate it down to 8 bits if its longer
The problem stems from s being a signed short. If s has a negative value, then s >> 8 causes implementation-defined behaviour.
Usually, implementations define that in this case bits shifted in from the left get filled with 1. So the result of 10000000 00000000 shifted 8 to the right will be 11111111 10000000, on those implementations. Now, applying & 255 will turn that into 00000000 10000000, the desired result.
As has been noted in comments, (assuming 8-bit chars and 2's complement arithmetic) converting to unsigned char has the same effect as & 255. Since the result is being stored in an unsigned char, the & 255 is redundant given those assumptions.
Another way to write this code, without relying on implementation-defined behaviour, would have been:
(unsigned short)s >> 8
Unsigned types always fill with zeroes when right-shifted.
A better way all round would be if the function both accepted and returned unsigned short. As it stands, return (c1 << 8) + 2 either causes undefined or implementation-defined behaviour if the returned value is greater than SHRT_MAX.

Left Bit Shift In C without extension

I was wondering how to get C to not extend my binary number when I bitshift to the left
int main ()
{
unsigned int binary_temp = 0b0100;
binary_temp = binary_temp << 2;
printf("%d", binary_temp);
return 0;
}
When I run that I want a return value of 0 since it has extended past the 4 digits I have, but right now it returns 16 (10000). How would I get C not to extend my number?
Edit: I would like to be able to work with the number in binary form so I need to have only 4 digits, and not just outputting the right number.
It does not extend your number but saves it as unsigned int type which is 4 bytes (32 bits) in size. You only fill the last 4 bits. To treat it as only 4 bits, use Bitwise AND with a Mask value. Here's example code:
int main()
{
unsigned int binary_temp = 0b0100;
binary_temp = (binary_temp << 2) & 0b1111;
printf("%u", binary_temp);
return 0;
}
You can bitwise AND the result with a 4 bit mask value:
binary_temp = (binary_temp << 2) & 0xF;
There is no 0b in standard C. You could use 4.
unsigned int /* prepare for wtf identifier: */
binary_temp = 4;
Left shifting by 2 is multiplying by 4. Why not?
binary_temp *= 4;
... and then reduce modulo 16?
binary_temp %= 16;
What sense is there to using binary operators, in this case? I see none.
The %d directive corresponds to an int argument, but the argument you're giving printf is an unsigned int. That's undefined behaviour.
printf("%u", binary_temp);
I'm sure whichever book you're reading will tell you about the %u directive.

How to detect in C whether your machine is 32-bits

So I am revising for an exam and I got stuck in this problem:
2.67 ◆◆
You are given the task of writing a procedure int_size_is_32() that yields 1
when run on a machine for which an int is 32 bits, and yields 0 otherwise. You are
not allowed to use the sizeof operator. Here is a first attempt:
1 /* The following code does not run properly on some machines */
2 int bad_int_size_is_32() {
3 /* Set most significant bit (msb) of 32-bit machine */
4 int set_msb = 1 << 31;
5 /* Shift past msb of 32-bit word */
6 int beyond_msb = 1 << 32;
7
8 /* set_msb is nonzero when word size >= 32
9 beyond_msb is zero when word size <= 32 */
10 return set_msb && !beyond_msb;
11 }
When compiled and run on a 32-bitSUNSPARC, however, this procedure returns 0. The following compiler message gives us an indication of the problem: warning: left shift count >= width of type
A. In what way does our code fail to comply with the C standard?
B. Modify the code to run properly on any machine for which data type int is
at least 32 bits.
C. Modify the code to run properly on any machine for which data type int is
at least 16 bits.
__________ MY ANSWERS:
A: When we shift by 31 in line 4, we overflow, bec according to the unsigned integer standard, the maximum unsigned integer we can represent is 2^31-1
B: In line 4 1<<30
C: In line 4 1<<14 and in line 6 1<<16
Am I right? And if not why please? Thank you!
__________ Second tentative answer:
B: In line 4 (1<<31)>>1 and in line 6: int beyond_msb = set_msb+1; I think I might be right this time :)
A: When we shift by 31 in line 4, we overflow, bec according to the unsigned integer standard, the maximum unsigned integer we can represent is 2^31-1
The error is on line 6, not line 4. The compiler message explains exactly why: shifting by a number of bits greater than the size of the type is undefined behavior.
B: In line 4 1<<30
C: In line 4 1<<14 and in line 6 1<<16
Both of those changes will cause the error to not appear, but will also make the function give incorrect results. You will need to understand how the function works (and how it doesn't work) before you fix it.
For first thing shifting by 30 will not create any overflow as max you can shift is word size w-1.
So when w = 32 you can shift till 31.
Overflow occurs when you shift it by 32 bits as lsb will now move to 33rd bit which is out of bound.
So the problem is in line 6 not 4.
For B.
0xffffffff + 1
If it is 32 bit then it will result 0 otherwise some nozero no.
There is absolutely no way to test the size of signed types in C at runtime. This is because overflow is undefined behavior; you cannot tell if overflow has happened. If you use unsigned int, you can just count how many types you can double a value that starts at 1 before the result becomes zero.
If you want to do the test at compile-time instead of runtime, this will work:
struct { int x:N; };
where N is replaced by successively larger values. The compiler is required to accept the program as long as N is no larger than the width of int, and reject it with a diagnostic/error when N is larger.
You should be able to comply with the C standard by breaking up the shifts left.
B -
Replace Line 6 with
int beyond_msb = (1 << 31) << 1;
C -
Replace Line 4 with
int set_msb = ((1 << 15) << 15) << 1 ;
Replace Line 6 with
int beyond_msb = ((1 << 15) << 15) << 2;
Also, as an extension to the question the following should satisify both B and C, and keep runtime error safe. Shifting left a bit at a time until it reverts back to all zeroes.
int int_size_is_32() {
//initialise our test integer variable.
int x = 1;
//count for checking purposes
int count = 0;
//keep shifting left 1 bit until we have got pushed the 1-bit off the left of the value type space.
while ( x != 0 ) {
x << 1 //shift left
count++;
}
return (count==31);
}

Resources