Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected types from UINT32_C, UINTN_C

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.

like image 361
chux - Reinstate Monica Avatar asked Aug 02 '17 16:08

chux - Reinstate Monica


1 Answers

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.)

like image 60
Keith Thompson Avatar answered Sep 29 '22 23:09

Keith Thompson