I read about the Microsoft specific suffix "i64" for integer constants. I want to do an UNsigned shift to a ULONGLONG.
ULONGLONG bigNum64 = 0x800000000000000i64 >> myval;
In normal C, I would use the suffix "U", e.g. the similar 32 bit operation would be
ULONG bigNum32 = 0x80000000U >> myval;
I do NOT want the 2's complement sign extension to propogate through the high bits. I want an UNSIGNED shift on a 64 bit const number. I think my first statement is going to do a SIGNED shift right.
I tried 0x800000000000000i64U
and 0x800000000000000u64
but got compiler errors.
You can use the suffix ull
, which is the standard (C99 and C++0x) way to specify an unsigned long long
integer literal, and a long long
is at least 64 bits.
Funny enough, but you don't actually need to add any suffix to your hex constant in order for it to be treated correctly. Section 6.4.4.1 of the C standard and section 2.14.3 of the C++ standard contain the following table:
Suffix | Decimal Constant | Octal or Hexadecimal Constant
-------------+------------------------+------------------------------
none | int | int
| long int | unsigned int
| long long int | long int
| | unsigned long int
| | long long int
| | unsigned long long int
-------------+------------------------+------------------------------
u or U | unsigned int | unsigned int
| unsigned long int | unsigned long int
| unsigned long long int | unsigned long long int
-------------+------------------------+------------------------------
l or L | long int | long int
| long long int | unsigned long int
| | long long int
| | unsigned long long int
-------------+------------------------+------------------------------
Both u or U | unsigned long int | unsigned long int
and l or L | unsigned long long int | unsigned long long int
-------------+------------------------+------------------------------
ll or LL | long long int | long long int
| | unsigned long long int
-------------+------------------------+------------------------------
Both u or U | unsigned long long int | unsigned long long int
and ll or LL | |
This table tells us what type an integer constant will have. The integer constant's type will be the first type in which it the value fits.
This means that the compiler will iterate through the following types for the hexadecimal constant 0x800000000000000
(it has no suffix, so it uses the "none" row, and it's a hex constant, so it uses the "Hexadecimal Constant" column), and it will use the first type which can store that value*:
int
: No, a 32-bit signed integer can't store this value.
unsigned int
: No, a 32-bit unsigned integer can't store this value.
long int
: No, a 32-bit signed integer can't store this value.
unsigned long int
: No, a 32-bit unsigned integer can't store this value.
long long int
: No, a 64-bit signed integer can't store this value.
unsigned long long int
: Yes, a 64-bit unsigned integer can store this value. Since this is the first type that can fully store the value, this is the type that the integer constant will have.
So, to answer your question of "How can I write and use the value 0x800000000000000
and make sure the compiler won't treat the high bit as a sign bit?": Simply just write unsigned long long value = 0x800000000000000
.
If you want to do some bitwise arithmetic with the value, you can just go ahead and do that (i.e. just write 0x800000000000000 >> myval
). You're guaranteed that it will not be treated as an overflowed signed integer, and your right shift won't do any sign extension because it's a positive value.
*I'm assuming that int
is 32-bits, long
is 32-bits, and long long
is 64-bits. Note that your compiler might use different bit sizes for these types, which may change the end result (though the process will still be the same).