0

I've been experimenting with C again after a while of not coding, and I have come across something I don't understand regarding bit shifting.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
void main()
{
    uint64_t x = 0;
    uint64_t testBin = 0b11110000;
    
    x = 1 << testBin;
    
    printf("testBin is %"PRIu64"\nx is %"PRIu64"\n", testBin, x);
    //uint64_t y = 240%32;
    //printf("%"PRIu64 "\n",y);
}

In the above code, x returns 65536, indicating that after bit shifting 240 places the 1 is now sat in position 17 of a 32-bit register, whereas I'd expect it to be at position 49 of a 64-bit register.

I tried the same with unsigned long long types, that did the same thing.

I've tried compiling both with and without the m64 argument, both the same.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Kierran Purden
  • 451
  • 1
  • 5
  • 15
  • 5
    dupe of [Unexpected C/C++ bitwise shift operators outcome](https://stackoverflow.com/questions/9860538/unexpected-c-c-bitwise-shift-operators-outcome) or any others about left-shifting by >= the width of the type. I dunno why you expected shifting by 240 places to do anything meaningful or predictable. – underscore_d Jun 04 '21 at 08:07
  • 3
    You have two problems in the code: First, `1 << testBin` operates on an `int` that is likely 32 bits in your C implementation, because it uses the type of `1`, which is `int`, not the type this expression is being assigned to, `uint64_t`. Second, the C standard defines `<<` only for shifts less than the width of the type of the left operand, and 240 is too big for either `int` or `uint64_t`. A correct expression for what you want would set the type of the left operand and reduce the right operand modulo the width, so `(uint64_t) 1 << testBin % 64;`. – Eric Postpischil Jun 04 '21 at 09:24
  • @underscore_d: This is not a duplicate because that question answers only one of the two problems here. – Eric Postpischil Jun 04 '21 at 09:25
  • 2
    duplicates of the first problem: [Why does shifting 0xff left by 24 bits result in an incorrect value?](https://stackoverflow.com/q/46246411/995714), [Why does 1ul << 64 return 1 instead of 0?](https://stackoverflow.com/q/37018469/995714), [Bitwise shift operation in C on uint64_t variable](https://stackoverflow.com/q/9290823/995714), [C left shift on 64 bits fail](https://stackoverflow.com/q/10048047/995714) – phuclv Jun 04 '21 at 10:18
  • 2
    duplicates of the second problem: [What does the C standard say about bitshifting more bits than the width of type?](https://stackoverflow.com/q/11270492/995714), [When can bit shifting cause undefined behaviour](https://stackoverflow.com/q/57617802/995714), [Shifting unsigned int more than the size of it, undefined or not?](https://stackoverflow.com/q/45432694/995714) – phuclv Jun 04 '21 at 10:18
  • @underscore_d I don’t expect it to do anything meaningful, it was merely a learning exercise. Originally it was `testBin << 1`, the result of which is obvious, I just wondered what would happen if I reversed it. I didn’t understand the result, thus the question – Kierran Purden Jun 04 '21 at 15:06

2 Answers2

1

In your setup the constant 1 is a 32 bit integer. Thus the expression 1 << testBin operates on 32 bits. You need to use a 64 bit constant to have the expression operate on 64 bits, e.g.:

x = (uint64_t)1 << testBin;

This does not change the fact that shifting by 240 bits is formally undefined behavior (even though it will probably give the expected result anyway). If testBin is set to 48, the result will be well-defined. Hence the following should be preferred:

x = (uint64_t)1 << (testBin % 64);
nielsen
  • 5,641
  • 10
  • 27
  • just use `1ULL`. No need to cast – phuclv Jun 04 '21 at 09:53
  • @phuclv That is a possibility, yes. I would say it is mostly a matter of taste/context. `unsigned long long` is specified to be *at least* 64 bits, but it may be more. In my opinion the explicit cast to a 64 bit integer is clearer in this case since that is the type the OP is using. – nielsen Jun 04 '21 at 11:54
-1

It happens because if the default integer type of the constant 1. It is integer (not long long integer). You need to use ULL postfix

x = 1ULL << testbin

PS if you want to shift 240 bits and your integer is less than it (maybe your implementation supports some giant inteters), it is an Undefined Behaviour)

0___________
  • 60,014
  • 4
  • 34
  • 74