Issue 1052: Relations between standard library integer types

Authors: Jay Ghiron
Date: 2026-05-01
Submitted against: C23
Status: Open

The minimum-width integer types do not properly consider padding or bit-precise integer types:

The typedef name int_leastN_t designates a signed integer type with a width of at least N, such that no signed integer type with lesser size has at least the specified width. Thus, int_least32_t denotes a signed integer type with a width of at least 32 bits.

The typedef name uint_leastN_t designates an unsigned integer type with a width of at least N, such that no unsigned integer type with lesser size has at least the specified width. Thus, uint_least16_t denotes an unsigned integer type with a width of at least 16 bits.

(C23 7.22.2.3 "Minimum-width integer types" paragraphs 1 and 2.)

For example:

#include<stdint.h>
static_assert(UINT_LEAST16_WIDTH<=UINT_LEAST32_WIDTH);

Considering these are supposed to be the minimum-width integer types it would be quite surprising if this static assertion might not hold. But consider the following implementation: there are no extended integer types, bytes are eight bits, short is sixty four bits with thirty two bits being padding, int is sixty four bits with zero bits being padding, long is more than sixty four bits with zero bits being padding, and long long is sixty four bits with zero bits being padding. The following definitions would then be valid:

typedef unsigned uint_least16_t;
typedef unsigned short uint_least32_t;

Meaning that UINT_LEAST16_WIDTH is sixty four and UINT_LEAST32_WIDTH is thirty two. The current wording only considers the size in terms of bytes, so it prevents sizeof(uint_least16_t)>sizeof(uint_least32_t) but it seems unintended to allow for the widths to be like this. It also means that for this hypothetical implementation:

typedef unsigned uint_least16_t;

There is no preference as to which type (unsigned or unsigned short) can be chosen, so even though there is a type with a smaller width uint_least16_t does not need to choose that type over the type with the larger width. That is weird, but it does not seem too important to try and restrict it. The fastest minimum-width integer types similarly do not constrain the widths to prevent larger values of N in int_fastN_t or uint_fastN_t from decreasing the width, and using padding bits is not even necessary to imagine an implementation that could use such weird definitions. It seems unintended too since if int_fastN_t were wider than int_fastM_t with N<M both types would be valid definitions for both int_fastN_t and int_fastM_t. To define them that way is to suggest that each type is faster than the other type, which does not make sense.

For the issue with bit-precise integer types consider the following implementation: there are no extended integer types, bytes are eight bits, short is thirty two bits, int is thirty two bits, long is sixty four bits, long long is sixty four bits, all standard integer types have no padding bits, and _BitInt(24) has no padding bits. int_least16_t should be defined either as short or as int, but because _BitInt(24) exists there is now an integer type with a lesser size than both that has at least sixteen bits in width. int_least16_t cannot be defined as a bit-precise integer type, since that is explicitly forbidden. Therefore it seems like such an implementation is forbidden and _BitInt(24) must be given padding bits, though that is surely unintended.

Suggested correction

Modify C23 7.22.2.1 paragraph 2:

In the following descriptions, the symbol N represents an unsigned decimal integer with no leading zeros (e.g. 8 or 24, but not 04 or 048). When two typedef names differing only in the value of N are defined, the typedef name whose N has a larger value shall not denote a type which has a width less than the width of the type which the other typedef name denotes.

Modify C23 7.22.2.3 paragraphs 1 and 2:

The typedef name int_leastN_t designates a signed integer type with a width of at least N, such that no signed integer type other than a bit-precise signed integer type with lesser size has at least the specified width. Thus, int_least32_t denotes a signed integer type with a width of at least 32 bits.

The typedef name uint_leastN_t designates an unsigned integer type with a width of at least N, such that no unsigned integer type other than a bit-precise unsigned integer type with lesser size has at least the specified width. Thus, uint_least16_t denotes an unsigned integer type with a width of at least 16 bits.