Reputation: 323
In my code there is no difference between uint16_t and uint32_t. Why?
I am using Raspbian on a RasPi with ARMv7 (32bit).
root@zentrale:/src# uname -a
Linux zentrale 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
This is the code:
void main()
{
uint16_t wert1;
uint32_t wert2;
int i;
wert1=2;
wert2=2;
for (i=0; i<33;i++)
{
printf("i: %2d\tLShifted wert1: %10u\t",i,wert1 << i);
printf("RShifted wert1: %10u\t",wert1 >> i);
printf("LShifted wert2: %10u\t",wert2 << i);
printf("RShifted wert2: %10u\n",wert2 >> i);
}
exit(0);
}
This is the stripped output:
i: 0 LShifted wert1: 2 RShifted wert1: 2 LShifted wert2: 2 RShifted wert2: 2
i: 1 LShifted wert1: 4 RShifted wert1: 1 LShifted wert2: 4 RShifted wert2: 1
[...]
i: 14 LShifted wert1: 32768 RShifted wert1: 0 LShifted wert2: 32768 RShifted wert2: 0
i: 15 LShifted wert1: 65536 RShifted wert1: 0 LShifted wert2: 65536 RShifted wert2: 0
i: 16 LShifted wert1: 131072 RShifted wert1: 0 LShifted wert2: 131072 RShifted wert2: 0
[...]
I would have expected the wert1 being 16bits getting to zero with the i=15 value as the name tells it is 16bits long.
Instead there is no difference in these two variables.
I found some reference of the max value of uint16_t in Raspian (see https://raspberry-projects.com/pi/programming-in-c/memory/variables)
So why is there no difference?
Thanks a lot!
Upvotes: 1
Views: 2913
Reputation: 134008
Both operands of <<
will undergo integer promotions, i.e. C11 6.3.11p2:
2 The following may be used in an expression wherever an int or unsigned int may be used:
- An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
- A bit-field of type _Bool, int, signed int, or unsigned int.
If an
int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint
; otherwise, it is converted to anunsigned int
.
Since int
on your platform is 32-bits wide, all values of uint16_t
are representable by int
. The uint32_t
is converted to unsigned int
!
Now the behaviours of both of them seem equal because GCC guarantees most of that! All signed arithmetic on all architectures that are supported by GCC use 2's complement; and additionally GCC does not consider the behaviour <<
on signed numbers as undefined in the cases where the sign bit is changed.
However, it is still undefined (even in GCC) what happens if the shift width is greater than or equal to the width of the operand (in this case 32 bits), so << 32
and << 33
will have undefined behaviour.
Other than that, in general the C standard says that the behaviour is undefined if a positive signed integer is left-shifted so that the sign bit changes! This happens when you shift a uint16_t
left by so many bits that it will change the shift bit of int
. Hence,
(uint16_t)0xFFFF << 16
has undefined behaviour on 32-bit platform, because the topmost bit is shifted to the sign bit of int
, whereas
(uint32_t)0xFFFF << 16
does not, because the latter would use unsigned arithmetic. As always, a compiler can define behaviour beyond that which is required by the standard.
Upvotes: 2
Reputation: 8614
There is no difference because of Integer Promotions
You should really go through this excellent post on the subject Implicit type promotion rules
Assuming 4 byte int
, before any arithmetic operation an uint16_t
is converted to a signed int
and then the operation is carried out.
A left shift on a negative number is undefined. But in this case, this number cannot be negative. So you will get the same output as a uint32_t
You should typecast the output of the left shift for proper operation.
Also, you are running the loop until i<33
. At i==32
, you will have undefined behaviour for uint32_t
and at i==31
you will have undefined behaviour for signed integer for uint16_t
printf("i: %2d\tLShifted wert1: %10u\t",i, (uint16_t)(wert1 << i);
Upvotes: 1