Shifting on Integer Constants shows warning. How to clear this? - c

Reference: Suffix in Integer Constants
unsigned long long y = 1 << 33;
Results in warning:
left shift count >= width of type [-Wshift-count-overflow]
Two Questions need to be cleared from the above context:
unsigned long long type has 64-bit, why cant we do left shift in it?
how shifting works in int constants('1')?

In C language, 1 is an int which is 32 bits on most platforms. When you try to shift it 33 bits before storing its value in an unsigned long long, that's not going to end well. You can fix this in 2 ways:
Use 1ULL instead, which is an unsigned long long constant:
unsigned long long y = 1ULL << 33;
Assign the value, then shift it:
unsigned long long y = 1;
y <<= 33;
Both are valid, but I'd suggest the first one since it's shorter and you can make y const.

Related

Why left shift 24 bits changed the value of unsigned long in C?

I expect 0b11010010 << 24 should be the same value as 0b11010010000000000000000000000000.
I tested it in C, 0b11010010 << 24 doesn't work as expected if we saved it in c unsigned long.
Does anyone know how C unsigned long works like this?
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
int main(){
unsigned long a = 0b11010010000000000000000000000000;
unsigned long b = 0b11010010 << 24;
bool isTheSame1 = a == b;
printf("isTheSame1 %d \n",isTheSame1);
bool isTheSame2 = 0b11010010000000000000000000000000 == (0b11010010 << 24);
printf("isTheSame2 %d",isTheSame2);
}
isTheSame1 should be 1 but it prints 0 as following
isTheSame1 0
isTheSame2 1
Compiled and executed by gcc main.c && ./a.out
gcc --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin22.2.0
Thread model: posix
Updated
As Allan Wind pointed out, I added UL suffix and now it works as expected.
unsigned long a = 0b11010010000000000000000000000000UL;
unsigned long b = 0b11010010UL << 24;
bool isTheSame1 = a == b;
printf("isTheSame1 %d \n",isTheSame1);
bool isTheSame2 = 0b11010010000000000000000000000000UL == (0b11010010UL << 24);
printf("isTheSame2 %d",isTheSame2);
The constant 0b11010010 has type int which is signed. Assuming an int is 32 bits, the expression 0b11010010 << 24 will shift a "1" bit into the sign bit. Doing so triggers undefined behavior which is why you're getting strange results.
Add the UL suffix to the constant to give it type unsigned long, then the shift will work as expected.
unsigned long b = 0b11010010UL << 24;
You are doing a left shift of a signed value (see good answer of #dbush)
In absence of suffixes numbers have int or double types
b = 0b11010010 ; /* type int */
b = 1.0; /* type double */
If you want want b in your example as unsigned long use a suffix:
b = 0b11010010UL; /* type unsigned long */
or a cast:
b = (unsigned long)0b11010010; /* type unsigned long */
With 32-bit (or smaller) int, 0b11010010 << 24 is undefined behaver (UB). It attempts to shift into the sign bit.
When int is 32-bit (common), this often results in a negative value corresponding to the bit pattern 11010010-00000000-00000000-00000000.
When a negative value is saved as an unsigned long, ULONG_MAX + 1 is added to it. With a 64-bit unsigned long the value has the bit pattern:
11111111-11111111-11111111-11111111-11010010-00000000-00000000-00000000
This large unsigned long in not equal to 0b11010010000000000000000000000000UL and so the output of "isTheSame1 0".
Had OP's long been 32-bit, it "might" have worked as OP had intended - yet unfortunately still replying on UB.
Appending an L
32-bit unsigned long: 0b11010010 << 24 suffers the same UB problem as above - yet might have "worked".
64-bit unsigned long: 0b11010010L is also long and 0b11010010L << 24 becomes the value 0b11010010000000000000000000000000, the same value as a.
Appending an U
32-bit unsigned: 0b11010010U << 24 becomes the value 0b11010010000000000000000000000000, the same value as a.
16-bit unsigned: 0b11010010U << 24 is undefined behavior as the shift is too great. Often the UB results in the same as 0b11010010U << (24-16), yet this is not reliably done.
Appending an UL
32 or 64-bit unsigned long: 0b11010010UL << 24 becomes the value 0b11010010000000000000000000000000, the same value as a.
Since the left hand side of the = of the below is unsigned long, better for the right hand side constant to be unsigned long.
unsigned long b = 0b11010010 << 24; // Original
unsigned long b = 0b11010010UL << 24; // Better

unsigned type of an integer constant

I got confused of the type of an integer constant, as described here:
On the first row, if a constant ended without 'u', why decimal constant must be signed type, while octal or hexadecimal constant can be an unsigned type?
I think that taking the constant as an unsigned version if the signed version do not fit has problem, for example:
long long l1 = 0xffffffff + 0xffffffff; // 0xffffffff is unsigned int
long long l2 = 4294967295 + 4294967295; // 4294967295 is signed long
l1 is fffffffe, while l2 is 1fffffffe. and obviously l1 is wrong
If I were to say, I'd answer with that hexadecimal and octal numbers represent bit pattern more closely than decimal ones, and therefore the C standard committee has decided that hex and oct numbers may be unsigned even without U suffix.
Think about how many people would write code like this:
uint32_t b = a & 0xFFFFFFF0;
uint32_t b = a & 4294967280; // or -15?
The issue causes problems more because of using wrong type for the operations than the constants not being the right type.
// some_wide_type = some_narrow_type + some_narrow_type --> trouble
long long l1 = 0xffffffff + 0xffffffff;
long long l2 = 4294967295 + 4294967295;
Instead do the math using the target type
long long l1 = 0LL + 0xffffffff + 0xffffffff;
long long l2 = 0LL + 4294967295 + 4294967295;
or use 1 type (long long) rather than the 3 (long long, unsigned long, long)
long long l1 = 0xffffffffLL + 0xffffffffLL;
long long l2 = 4294967295LL + 4294967295LL;

How do I mute this error : "integer literal is too large to be represented in a signed integer type"

I have this school assignment in C where I will be corrected with the following flags :
-Wall -Wextra -Werror
So this harmless warning becomes an error and prevents compilation :
integer literal is too large to be represented in a signed integer type
(code still works) but if I can't mute it my work will be considered wrong
Here is my code :
static unsigned long long piece_to_map(unsigned short little)
{
static unsigned short row;
unsigned long long big;
char i;
unsigned long long mask_left;
unsigned long long mask_top;
mask_left = 9259542123273814144;
mask_top = 18374686479671623680;
row = 15;
big = 0;
i = 0;
while (i < 16)
{
big |= (little & (row << i)) << i;
i += 4;
}
while ((big & mask_t) == 0)
big = big << 8;
while ((big & mask_l) == 0)
big = big << 1;
return (big);
}
What I'm trying to achieve here is to transform an unsigned short (representing a shape in a 4x4 square) to an unsigned long long representing the same shape in a 8x8 square having the shape cornered top-left. It works perfectly and according to my expectations, I just need to avoid having the warning. I was formerly using the (normally equivalent) binary expression instead and didn't get any warning
0b1111111100000000000000000000000000000000000000000000000000000000
and
0b1000000010000000100000001000000010000000100000001000000010000000
The problem is that the 0bxxxx form is not standard C (As I read in this StackOverflow answer), therefore I am not allowed to use it.
I also tried
mask_left = (unsigned long long)9259542123273814144;
mask_top = (unsigned long long)18374686479671623680;
The compiler still tells me that the value is too large to be represened in a signed integer type. What am I doing wrong ? Is there any way to fix this at all ?
Implicitly, the integer literal is signed and of course the values are too big for a signed long long, so you need to let the compiler know that they have type unsigned, like this
mask_left = 9259542123273814143U;
mask_top = 18374686479671623680U;
Rewrite it with explicit size:
mask_left = 9259542123273814144uLL;
mask_top = 18374686479671623680uLL;
By writing it as (unsigned long long) 9259542123273814144 it means to take the integer and then cast it longer. Unfortunately, the integer is probably munged (by throwing away the higher bits to make it an int) and then increasing the size.
Signed integer literals cannot be larger than 2147483648. For a number larger than that, you need to add the LL prefix, which tells the compiler it is a long long. In your case, you want ULL as that designates an unsigned long long, which is what you're assigning to.

How do you perform an XOR operation of a unsigned long integer and a character array in C?

These are my two variables with which I want to do an xor operation (in C).
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
desired output= 0X45D6AC3D
I know I cannot do a simple z ^ array, because it's a character array and not a single character. Will I have to do XOR of one byte at a time or is there a function for it in C?
I am trying all kinds of crazy things to get it done, but failing miserably all the time. If anyone can help me out with a small code snippet or at least a rough idea, I would be extremely grateful.
Cast the array, which is treat as a pointer to the first element in an expression like this one, to a long pointer instead of char pointer , and dereference it.
unsigned long result = z ^ *(unsigned long *)array;
Just make an unsigned long int out of your array (warning, it depends on the machine endianness!):
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
unsigned long int w = 0;
w |= array[0] << 0;
w |= array[1] << 8;
w |= array[2] << 16;
w |= array[3] << 24;
unsigned long output = z ^ w;
unsigned long int z=0xB51A06CD;
unsigned char array[] = {0xF0,0xCC,0xAA,0xF0};
unsigned long tmp;
memcpy(&tmp, array, sizeof tmp);
... z ^ tmp ...
Note that this still makes a number of non-portable assumptions: that unsigned long is 4 bytes, and that the system's endianness is what you expect it to be.
As others mentioned, you have to worry about endianness and size of long. Here's how to make it safe:
1) instead of unsigned long, use uint32_t (from inttypes.h), to be sure you get a 4 byte type.
2) use htonl() as a platform-independent way to ensure you interpret the array as a big-endian value
z ^ htonl(*(uint32_t *)array);

Concatenate two 32bit numbers to get a 64bit result

I need to concatenate two hexadecimal numbers 32 bits each each, to get a final result of 64 bits.
I tried the following code but didn't get a good result:
unsigned long a,b;
unsigned long long c;
c = (unsigned long long) (a << 32 | b);
Can anybody help me please?
Thanks.
Use proper fixed size types and be careful about type promotion and operator precedence, e.g.
#include <stdint.h>
uint32_t a, b;
uint64_t c;
c = ((uint64_t)a << 32) | b;
You need to cast a to long long before shifting it:
unsigned long long c = ((unsigned long long)a << 32 | b);
Shortest form is:
c = a+0ULL<<32|b
The third line should be changed to
((unsigned long long)a) << 32 | ((unsigned long long) b)
What your current code is doing, is taking the 32-bit variable a and shifting it 32 bits to the left (making its value 0, because the bottom 32 bits are all empty), then or-ing it with the 32-bit variable b.
What the changed version does is to case the 32-bit variable a to 64 bits, shift it 32 bits to the left, cast the 32-bit variable b to 64 bits, then or the two 64-bit variables together. The result is naturally 64 bits.
I would imagine that this would do the trick:
typedef unsigned long U64 ; // your unsigned 64-bit int typedef here
typedef unsigned int U32 ; // your unsigned 32-bit int typedef here
U64 join( U32 a , U32 b )
{
U64 result = ((U64)a) << 32
| ((U64)b)
;
return result ;
}
I'll leave to you to divine the appropriate typedefs for U64 and U32.

Resources