0Day Forums
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.)