6

Very simple questions guys, but maybe I'm just forgetting something. In 64bit linux, a long is 8bytes correct? If that's the case, and I want to set the 64th bit, I can do the following:

unsigned long num = 1<<63;

Whenever I compile this, however, it gives me an error saying that I'm left shifting by more than the width. Also, if I wanted to take the first 32bits of a long type (without sign extension), can I do:

num = num&0xFFFFFFFF;

or what about:

num = (int)(num);

Thank you.

de1337ed
  • 3,113
  • 12
  • 37
  • 55
  • 5
    Try: `unsigned long num = 1UL << 63;` – Mysticial Apr 06 '12 at 06:48
  • @Mysticial Why not post that as an answer along with the integer promotion stuff ? – cnicutar Apr 06 '12 at 06:50
  • 1
    Depends on which compiler is used. Many legacy compilers would default to 32-bit longs, even if running on a 64 bit machine. Confirm that `sizeof num` is 8 before trying anything more. – wallyk Apr 06 '12 at 06:51
  • 2
    @cnicutar After you answer the same questions many times over and over again, it starts to get old. So I can't get myself to do more than a 1 line answer... – Mysticial Apr 06 '12 at 06:51
  • possible duplicate of [warning: left shift count >= width of type](http://stackoverflow.com/questions/4201301/warning-left-shift-count-width-of-type) – Bo Persson Apr 06 '12 at 12:51

5 Answers5

4

Actually, if you want some precise length (in bits) for your integers, assuming a C99 conforming compiler, #include <stdint.h> and use types like int64_t, int32_t etc. A handy type is intptr_t, an integer type with the same number of bits as void* pointer (so you can call it a machine "word")

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 2
    A C99 conforming compiler does not need to provide fixed-width types, e.g. `int64_t`. Only twos-complement implementations that provide 8-, 16-, 32- and 64-bit integer types with no padding bits are *required* to provide fixed-width integer typedefs in `stdint.h`. – dreamlax Apr 06 '12 at 07:29
4

In 64bit linux, a long is 8bytes correct?

Need not be. Depends on the compiler than on the underlying OS. Check this for a nice discussion. What decides the sizeof an integer?

Whenever I compile this, however, it gives me an error saying that I'm left shifting by more than the width

Everyone have already answered this. Use 1UL

Also, if I wanted to take the first 32bits of a long type (without sign extension), can I do:

num = num&0xFFFFFFFF;
or what about:

num = (int)(num);

num = num&0xFFFFFFFF. This will give you the lower 32-bits. But note that if long is just 4 bytes on your system then you are getting the entire number. Coming to the sign extension part, if you've used a long and not unsigned long then you cannot do away with the sign extended bits. For example, -1 is represented as all ones, right from the 0th bit. How will you avoid these ones by masking?

num = (int)(num) will give you the lower 32-bits but compiler might through a Overflow Exception warning if num does not fit into an int

Community
  • 1
  • 1
Pavan Manjunath
  • 27,404
  • 12
  • 99
  • 125
  • 4
    Agree that C doesn't impose `long` to be 64bits on 64bits machine. However linux implicitely imposes that. Just take a look at all the pointers passed through `unsigned long` and memory offsets passed through `signed long` (e.g. syscalls). Thus, as long as linux source code doesn't drastically change, I would answer **yes** to the question : "In 64bit linux, a `long` is 8bytes, correct ?" – Yves Lhuillier Jan 13 '17 at 09:14
1

For portability you could use:

limits.h

#define LNG_BIT   (sizeof(long) * CHAR_BIT)

unsigned long num = 1UL << (LNG_BIT - 1);

To get "low int", something like?:

#define INT_BIT   (sizeof(int) * CHAR_BIT)

if (LNG_BIT > INT_BIT)
    return num & (~0UL >> INT_BIT);
else
    return num;

or

    num &= ~(~0U << INT_BIT);

Or, use mask, etc. Depends in large on why, for what, etc. you want the int bits.

Also notice the options given by compilers; I.e. if you are using gcc:

-m32
-m64
-mx32
        Generate code for a 32-bit or 64-bit environment.
        * The -m32 option sets int, long, and pointer types to 32 bits, and generates code that runs on any i386 system.
        * The -m64 option sets int to 32 bits and long and pointer types to 64 bits, and generates code for the x86-64 architecture. For Darwin only the -m64 option also turns off the -fno-pic and -mdynamic-no-pic options.
        * The -mx32 option sets int, long, and pointer types to 32 bits, and generates code for the x86-64 architecture.

There is also -maddress-mode=long etc.

-maddress-mode=long
        Generate code for long address mode. This is only supported for 64-bit and x32 environments. It is the default address mode for 64-bit environments.

Morpfh
  • 4,033
  • 18
  • 26
1

AFAIR code like this was the source of a major bug in reiserfs a few years ago:

unsigned long num = 1<<63;

If you speak of x86_64, yes, a long is 64 bit and on most other 64 bit linux plytforms too. The problem is that both the 1 and the 63 in your code are simple int, and so the result is undefined. Better use

 unsigned long num = 1UL<<63;

or

 unsigned long num = (unsigned long)1<<63;
Gunther Piez
  • 29,760
  • 6
  • 71
  • 103
0

I believe the problem with the first question is that the compiler is treating '1' as an integer, not as a long integer. It doesn't figure it out until after the assignment.

You can fix it by doing:

unsigned long num = (unsigned long)1<<63;
Michael
  • 2,181
  • 14
  • 14
  • Do you happen to know the answer to the second part of my question. About obtaining the non-sign-extended first 32 bits? Would I do something similar to num = num&(unsigned long)(0xFFFFFFFF)? – de1337ed Apr 06 '12 at 07:48
  • @de1337ed I've answered the second part of your Qn. Check out – Pavan Manjunath Apr 06 '12 at 09:52