Unexpected types from UINT32_C, UINTN_C - Printable Version +- 0Day Forums (https://zeroday.vip) +-- Forum: Coding (https://zeroday.vip/Forum-Coding) +--- Forum: C & C++ (https://zeroday.vip/Forum-C-C) +--- Thread: Unexpected types from UINT32_C, UINTN_C (/Thread-Unexpected-types-from-UINT32-C-UINTN-C) |
Unexpected types from UINT32_C, UINTN_C - pregrade753214 - 07-27-2023 > **7.20.4.1 Macros for minimum-width integer constants** > ... The macro `UINTN_C(value)` shall expand to an integer constant expression corresponding to the type `uint_leastN_t`. For example, if `uint_least64_t` is a name for the type `unsigned long long int`, then `UINT64_C(0x123`) might expand to the integer constant `0x123ULL`. C11dr §7.20.4.1 1 The type of `UINTN_C()` and friends are not as expected. See "Expected" comments in code output. A) Is my compiler implementation wrong and the constant type should be `uint_leastN_t`? or B) Should the type of constant from `UINTN_C(value)` be the minimum of `uint_leastN_t`, `int`, `unsigned` and type needed to encode the value? or C) something else? --- I had expected that the type of the constants to correspond to `uint_leastN_t`, but it appears to not be so under 2 conditions: **1 If the macro corresponding type is below `int/unsigned`, the constant is `int/unsigned` **2 If the value exceeds the range of the `uint_leastN_t`, then the type becomes a wider type constant. > §6.4.4.1 "The type of an integer constant is the first of the corresponding list in which its value can be represented ... (long list follows). --- #include <limits.h> #include <stdio.h> #define type_of(X) _Generic((X), \ unsigned long long: "unsigned long long", \ unsigned long: "unsigned long", \ unsigned: "unsigned", \ int: "int", \ unsigned short: "unsigned short", \ default: "?" \ ) int main() { uint_least16_t u16 = 0; uint_least32_t u32 = 0; uint_least64_t u64 = 0; printf("%zu %s\n", sizeof(u16), type_of(u16)); printf("%zu %s\n", sizeof(u32), type_of(u32)); printf("%zu %s\n", sizeof(u64), type_of(u64)); puts(""); printf("%zu %s\n", sizeof((uint_least16_t) UINT16_C(0)), type_of((uint_least16_t) UINT16_C(0))); printf("%zu %s\n", sizeof UINT16_C(0), type_of(UINT16_C(0))); printf("%zu %s\n", sizeof UINT16_C(0x1234), type_of(UINT16_C(0x1234))); printf("%zu %s\n", sizeof UINT16_C(0x12345), type_of(UINT16_C(0x12345))); printf("%zu %s\n", sizeof UINT32_C(0x12345678), type_of(UINT32_C(0x12345678))); printf("%zu %s\n", sizeof UINT32_C(0x123456789), type_of(UINT32_C(0x123456789))); return 0; //round_frac_test(-2.05446162500000000e+06, 205); round_frac_test(fp_rand(), 6); round_frac_tests(10000); puts("Done"); return 0; } Output 2 unsigned short 4 unsigned 8 unsigned long long 2 unsigned short 4 int // Expected 2 unsigned short, see **1 4 int // Expected 2 unsigned short, see **1 4 int // Expected 2 unsigned short, see **2 4 unsigned 8 unsigned long long // Expected 4 unsigned, see **2 I am using (GNU C11 (GCC) version 5.4.0) In forming this post, I am leaning toward B, yet I am looking for your rational for confirmation -one way of the other. If B is so, a disappointing part is that `UINTN_C()` could result in a _signed_ type. I suppose that is what the "minimum-width" part is about. RE: Unexpected types from UINT32_C, UINTN_C - quahog987542 - 07-27-2023 On the host with 16 bit `short` and 32 bit integers `unsigned short` numbers are promoted to `int`. So the those macros are defined as: #define INT16_C(x) (x) #define UINT16_C(x) (x) with no 'U' suffix for the `unsigned short` > 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 an int; Those macros do only expand to the corresponding constants (or integer literals using more common language), and do not create any objects, and have to usable in the #if. So no casts are allowed. Macros do not perform any range checks as well. int16_t x0 = 123; uint16_t x1 = 123; // no sign suffix needed int32_t x2 = 2147483647; uint32_t x3 = 2147583647U; //sign suffix theoreticaly needed as int and unsigned int have the same rank int64_t x4 = 9223372036854775807LL; uint64_t x5 = 9237372036854775807ULL; //same as above RE: Unexpected types from UINT32_C, UINTN_C - kalvn845 - 07-27-2023 This is covered in the parent subsection, 7.20.4. In the portion you quoted: > The macro `UINTN_C(value)` shall expand to an integer constant expression corresponding to the type `uint_leastN_t`. it says "*corresponding to*", not that the expansion is actually *of* that type. The meaning of "*corresponding to*" is explained in 7.20.4p3: > Each invocation of one of these macros shall expand to an integer > constant expression suitable for use in **`#if`** preprocessing directives. > The type of the expression shall have the same type as would an > expression of the corresponding type converted according to the > integer promotions. The value of the expression shall be that of the > argument. Since the macros are intended to be used in an `#if` directive, they cannot use casts (the preprocessor doesn't understand casts or type names). In practice, such a constant expression will almost always be implicitly converted to the appropriate type, so the fact that its actual type differs from what you might expect is not generally a problem. As for a value outside the range of `uint_leastN_t`, that's also covered in the parent subsection, in 7.20.4p2: > The argument in any instance of these macros shall be an unsuffixed > integer constant (as defined in 6.4.4.1) with a value that does not > exceed the limits for the corresponding type. This is a "shall" outside a constraint, so violating it causes undefined behavior. Don't do that. (When reading the C standard, it's generally a good idea to check the parent subsections for wording that might clarify or override what you're reading. I've been bitten by this myself.) |