How do you specify a 64 bit unsigned int const 0x8

2020-03-08 09:10发布

问题:

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.

回答1:

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.



回答2:

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*:

  1. int: No, a 32-bit signed integer can't store this value.
  2. unsigned int: No, a 32-bit unsigned integer can't store this value.
  3. long int: No, a 32-bit signed integer can't store this value.
  4. unsigned long int: No, a 32-bit unsigned integer can't store this value.
  5. long long int: No, a 64-bit signed integer can't store this value.
  6. 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).