bit shifting in C, unexpected result - c

when i pass n = 0x0, i get 0xffffffff on the screen
which i expect should be 0x00000000
as i shift the word by 32 bits
(Just Ignore the x! I didn't use it inside the function.)
void logicalShift(int x, int n) {
int y = 32;
int mask = 0xffffffff;
printf("mask %x", mask << (y-n));
}
One of the interesting point is
void logicalShift(int x, int n) {
int y = 32;
int mask = 0xffffffff;
printf("mask %x", mask << 32);
}
this will output what i expected. Do i miss out anything?
Thank you!
Im running on ubuntu

A shift left of 32 bits on a 32 bit value has undefined results. You can only shift 0 to 31 bits.
See also here: Why doesn't left bit-shift, "<<", for 32-bit integers work as expected when used more than 32 times?

Here is the relevant quote from the C11 draft §6.5.7.3;
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.
In other words, the result is undefined, and the compiler is free to generate any result.

Related

Extract k bits from any side of hex notation

int X = 0x1234ABCD;
int Y = 0xcdba4321;
// a) print the lower 10 bits of X in hex notation
int output1 = X & 0xFF;
printf("%X\n", output1);
// b) print the upper 12 bits of Y in hex notation
int output2 = Y >> 20;
printf("%X\n", output2);
I want to print the lower 10 bits of X in hex notation; since each character in hex is 4 bits, FF = 8 bits, would it be right to & with 0x2FF to get the lower 10 bits in hex notation.
Also, would shifting right by 20 drop all 20 bits at the end, and keep the upper 12 bits only?
I want to print the lower 10 bits of X in hex notation; since each character in hex is 4 bits, FF = 8 bits, would it be right to & with 0x2FF to get the lower 10 bits in hex notation.
No, that would be incorrect. You'd want to use 0x3FF to get the low 10 bits. (0x2FF in binary is: 1011111111). If you're a little uncertain with hex values, an easier way to do that these days is via binary constants instead, e.g.
// mask lowest ten bits in hex
int output1 = X & 0x3FF;
// mask lowest ten bits in binary
int output1 = X & 0b1111111111;
Also, would shifting right by 20 drop all 20 bits at the end, and keep the upper 12 bits only?
In the case of LEFT shift, zeros will be shifted in from the right, and the higher bits will be dropped.
In the case of RIGHT shift, it depends on the sign of the data type you are shifting.
// unsigned right shift
unsigned U = 0x80000000;
U = U >> 20;
printf("%x\n", U); // prints: 800
// signed right shift
int S = 0x80000000;
S = S >> 20;
printf("%x\n", S); // prints: fffff800
Signed right-shift typically shifts the highest bit in from the left. Unsigned right-shift always shifts in zero.
As an aside: IIRC the C standard is a little vague wrt to signed integer shifts. I believe it is theoretically possible to have a hardware platform that shifts in zeros for signed right shift (i.e. micro-controllers). Most of your typical platforms (Intel/Arm) will shift in the highest bit though.
Assuming 32 bit int, then you have the following problems:
0xcdba4321 is too large to fit inside an int. The hex constant itself will actually be unsigned int in this specific case, because of an oddball type rule in C. From there you force an implicit conversion to int, likely ending up with a negative number.
Y >> 20 right shifts a negative number, which is non-portable behavior. It can either shift in ones (arithmetic shift) or zeroes (logical shift), depending on compiler. Whereas right shifting unsigned types is well-defined and always results in logical shift.
& 0xFF masks out 8 bits, not 10.
%X expects an unsigned int, not an int.
The root of all your problems is "sloppy typing" - that is, writing int all over the place when you actually need a more suitable type. You should start using the portable types from stdint.h instead, in this case uint32_t. Also make a habit of always ending you hex constants with a u or U suffix.
A fixed program:
#include <stdio.h>
#include <stdint.h>
int main (void)
{
uint32_t X = 0x1234ABCDu;
uint32_t Y = 0xcdba4321u;
printf("%X\n", X & 0x3FFu);
printf("%X\n", Y >> (32-12));
}
The 0x3FFu mask can also be written as ( (1u<<10) - 1).
(Strictly speaking you need to printf the stdint.h types using specifiers from inttypes.h but lets not confuse the answer by introducing those at the same time.)
Lots of high-value answers to this question.
Here's more info that might spark curiosity...
int main() {
uint32_t X;
X = 0x1234ABCDu; // your first hex number
printf( "%X\n", X );
X &= ((1u<<12)-1)<<20; // mask 12 bits, shifting mask left
printf( "%X\n", X );
X = 0x1234ABCDu; // your first hex number
X &= ~0u^(~0u>>12);
printf( "%X\n", X );
X = 0x0234ABCDu; // Note leading 0 printed in two styles
printf( "%X %08X\n", X, X );
return 0;
}
1234ABCD
12300000
12300000
234ABCD 0234ABCD
print the upper 12 bits of Y in hex notation
To handle this when the width of int is not known, first determine the width with code like sizeof(unsigned)*CHAR_BIT. (C specifies it must be at least 16-bit.)
Best to use unsigned or mask the shifted result with an unsigned.
#include <limits.h>
int output2 = Y;
printf("%X\n", (unsigned) output2 >> (sizeof(unsigned)*CHAR_BIT - 12));
// or
printf("%X\n", (output2 >> (sizeof output2 * CHAR_BIT - 12)) & 0x3FFu);
Rare non-2's complement encoded int needs additional code - not shown.
Very rare padded int needs other bit width detection - not shown.

Bizarre right bitshift inconsistency

I've been working with bits in C (running on ubuntu). In using two different ways to right shift an integer, I got oddly different outputs:
#include <stdio.h>
int main(){
int x = 0xfffffffe;
int a = x >> 16;
int b = 0xfffffffe >> 16;
printf("%X\n%X\n", a, b);
return 0;
}
I would think the output would be the same for each: FFFF, because the right four hex places (16 bits) are being rightshifted away. Instead, the output is:
FFFFFFFF
FFFF
What explains this behaviour?
When you say:
int x = 0xfffffffe;
That sets x to -2 because the maximum value an int can hold here is 0x7FFFFFFF and it wraps around during conversion. When you bit-shift the negative number it gets weird.
If you change those values to unsigned int it all works out.
#include <stdio.h>
int main(){
unsigned int x = 0xfffffffe;
unsigned int a = x >> 16;
unsigned int b = 0xfffffffe >> 16;
printf("%X\n%X\n", a, b);
return 0;
}
The behaviour you see here has to do with shifting on signed or unsigned integers which give different results.
Shifts on unsigned integers are logical. On the contrary, shift on signed integers are arithmetic. EDIT: In C, it's implementation defined but generally the case.
Consequently,
int x = 0xfffffffe;
int a = x >> 16;
this part performs an arithmetic shift because x is signed. And because x is actually negative (-2 in two's complement), x is sign extended, so '1's are appended which results in 0xFFFFFFFF.
On the contrary,
int b = 0xfffffffe >> 16;
0xfffffffe is a litteral interpreted as an unsigned integer. Therefore a logical shift of 16 results in 0x0000FFFF as expected.

Unusual behavior with shift-right bitwise operator

I'm writing a simple code in C (only using bit-wise operators) that takes a pointer to an unsigned integer x and flips the bit at the nth position n in the binary notation of the integer. The function is declared as follows:
int flip_bit (unsigned * x, unsigned n);
It is assumed that n is between 0 and 31.
In one of the steps, I perform a shift-right operation, but the results are not what I expect. For instance, if I do 0x8000000 >> 30, I get 0xfffffffe as a result, which are 1000 0000 ... 0000 and 1111 1111 ... 1110, respectively, in binary notation. (The expected result is0000 0000 ... 0010).
I am unsure of how or where I am making the mistake. Any help would be appreciated. Thanks.
Edit 1: Below is the code.
#include <stdio.h>
#define INTSIZE 31
void flip_bit(unsigned * x,
unsigned n) {
int a, b, c, d, e, f, g, h, i, j, k, l, m, p, q;
// save bits on the left of n and insert a zero at the end
a = * x >> n + 1;
b = a << 1;
// save bits on the right of n
c = * x << INTSIZE - (n - 1);
d = c >> INTSIZE - (n - 1);
// shift the bits to the left (back in their positions)
// combine all bits
e = d << n;
f = b | e;
// Isolating the nth bit in its position
g = * x >> n;
h = g << INTSIZE;
// THIS LINE BELOW IS THE ONE CAUSING TROUBLE.
i = h >> INTSIZE - n;
// flipping all bits and removing the 1s surrounding
// the nth bit (0 or 1)
j = ~i;
k = j >> n;
l = k << INTSIZE;
p = l >> INTSIZE - n;
// combining the value missing nth bit and
// the one with the flipped one
q = f | p;
* x = q;
}
I'm getting the unusual behavior when I run flip_bit(0x0000004e,0). The line for the shift-right operation in question has comments in uppercase above it.
There is probably a shorter way to do this (without using a thousand variables), but that's what I have now.
Edit 2: The problem was that I declared the variables as int (instead of unsigned). Nevertheless, that's a terrible way to solve the question. #old_timer suggested returning *x ^ (1u << n), which is much better.
The issue here is that you're performing a right shift on a signed int.
From section 6.5.7 of the C standard:
5 The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative
value, the value of the result is the integral part of the quotient of
E1 / 2E2. If E1 has a signed type and a negative value, the
resulting value is implementation-defined.
The bold part is what's happening in your case. Each of your intermediate variables are of type int. Assuming your system uses 2's complement representations for negative numbers, any int value with the high bit set is interpreted as a negative value.
The most common implementation-defined behavior behavior you'll see (and this in fact what gcc and MSVC both do) in this case is that if the high bit is set on a signed value then a 1 will be shifted in on a right shift. This preserves the sign of the value and makes x >> n equivalent to x / 2n for all signed and unsigned values.
You can fix this by changing all of your intermediate variables to unsigned. That way, they match the type of *x and you won't get 1s pushed on to the left.
As for your method of flipping a bit, there is a much simpler way of doing so. You can instead use the ^ operator, which is the bitwise exclusive OR operator.
From section 6.5.11 of the C standard:
4 The result of the ^ operator is the bitwise exclusive OR (XOR) of the
operands (that is, each bit in the result is set if and only if
exactly one of the corresponding bits in the converted operands is
set).
For example:
0010 1000
^ 1100 ^ 1101
------ ------
1110 0101
Note that you can use this to create a bitmask, then use that bitmask to flip the bits in the other operand.
So if you want to flip bit n, take the value 1, left shift it by n to move that bit to the desired location then XOR that value with your target value to flip that bit:
void flip_bit(unsigned * x, unsigned n) {
return *x = *x ^ (1u << n);
}
You can also use the ^= operator in this case which XORs the right operand to the left and assigns the result to the left:
return *x ^= (1u << n);
Also note the u suffix on the integer constant. That causes the type of the constant to be unsigned which helps to avoid the implementation defined behavior you experienced.
#include <stdio.h>
int main ( void )
{
unsigned int x;
int y;
x=0x80000000;
x>>=30;
printf("0x%08X\n",x);
y=0x80000000;
y>>=30;
printf("0x%08X\n",y);
return(0);
}
gcc on mint
0x00000002
0xFFFFFFFE
or what about this
#include <stdio.h>
int main ( void )
{
unsigned int x;
x=0x12345678;
x^=1<<30;
printf("0x%08X\n",x);
}
output
0x52345678

why the left shift operation does not work

I have the following code, where we try to left shift certain bits of a value:
int main()
{
unsigned long mvb = 1;
mvb << 8;
printf("The shift value is %u\n", mvb);
mvb << 56;
printf("The shift value is %u\n", mvb);
}
but the result for all those two operation are both 1, what is the reason, and how use it correctly?
You need to assign it back to mvb after shifting like:
mvb = mvb << 8;
%u is the wrong format specifier for an unsigned long, so the program behaviour is undefined. Use %lu instead.
Note that you're not actually changing the value of mvb: the printf calls are operating on the original value of mvb.
Writing mvb <<= 8 is the fix (this is using the bitwise left shift assigment operator), but be careful not to apply a shift beyond the number of bits in your type, as that is also undefined behaviour in C. For the avoidance of doubt, the undefined behaviour only results if you shift by too many bits at once: the subsequent shift of mvb <<= 56 is fine for a 64 bit type, but mvb <<= (8 + 56) would not be.

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.

Resources