07-27-2023, 12:12 PM
> **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.
> ... 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.