Unexpected types from UINT32_C, UINTN_C

7.20.4.1 Macros for integer constants of minimum width
... The macro UINTN_C(value) will expand to an integer constant expression corresponding to the type uint_leastN_t . For example, if uint_least64_t is the name for the type unsigned long long int , then UINT64_C(0x123 ) can expand to the integer constant 0x123ULL . C11dr Β§7.20.4.1 1

Type UINTN_C() and friends are not as expected. See "Expected" comments in the code output.

A) Is my compiler implementation wrong and should the constant type be uint_leastN_t ?
or
B) Should the constant type from UINTN_C(value) be the minimum value of uint_leastN_t , int , unsigned and the type needed to encode the value?
or
C) something else?


I was expecting the constant type to match uint_leastN_t , but it seems this is not the case under two conditions:

** 1 If the corresponding macro type is below int/unsigned , the constant is int/unsigned

** 2 If the value exceeds the range 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; } 

Exit

 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)

By creating this post, I lean toward B, but I'm looking for your reasonable confirmation to confirm a different path. If B is so, the disappointing part is that UINTN_C() can lead to a signed type.

I believe that this is what we are talking about the part of the minimum width.

+7
c language-lawyer
source share
2 answers

This is described in parent section 7.20.4.

In the part that you indicated:

The macro UINTN_C(value) must be expanded to an integer constant expression> corresponding to the type uint_leastN_t .

he says β€œconsistent,” not that the decomposition is really of this type. The value of "match" is explained in 7.20.4p3:

Each call to one of these macros must expand to an integer constant expression, suitable for use in #if preprocessing #if . The type of expression must be of the same type as the expression of the corresponding type, converted in accordance with whole promotions. The value of the expression must match the value of the argument.

Since macros are intended for use in the #if directive, they cannot use casts (the preprocessor does not understand casts or type names).

In practice, such a constant expression will almost always be implicitly converted to the corresponding type, so the fact that its actual type is different from what you might expect is usually not a problem.

As for the value outside the range uint_leastN_t , which is also considered in the parent unit, at 7.20.4p2:

The argument in any instance of these macros must be an unconfirmed integer constant (as defined in 6.4.4.1) with a value that does not exceed the limits for the corresponding type.

This "will be" out of limitation, therefore its violation causes undefined behavior. Do not do this.

(When reading standard C, it is usually recommended that you check the parent subkeys for wording that can clarify or override what you read. I was bitten by it myself.)

+9
source share

On a host with 16-bit short and 32-bit integers, unsigned short raised to int . Thus, these macros are defined as:

 #define INT16_C(x) (x) #define UINT16_C(x) (x) 

without the suffix 'U' for unsigned short

If int can represent all values ​​of the original type (as limited in width for a bit field), the value is converted to int;

These macros expand only to the corresponding constants (or integer literals using a more general language) and do not create any objects and can be used in #if. Thus, no shots are allowed. Macros also do not perform any range checks.

 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 
0
source share

All Articles