简体   繁体   中英

Are intmax_t and uintmax_t guaranteed to be of the same size?

I need to know if intmax_t is always "the same type" as uintmax_t except using two's complement instead of unsigned value.

Or putting this in formal terms, will the code below always compile in a standard-compliant compiler?

#include <cstdint>

// The important assertion:
static_assert(sizeof(std::uintmax_t) == sizeof(std::intmax_t));
// Less important assertions:
static_assert(UINTMAX_MAX == static_cast<std::uintmax_t>(INTMAX_MAX) * 2 + 1);
static_assert(-static_cast<std::intmax_t>(UINTMAX_MAX / 2) - 1 == INTMAX_MIN);

I'm particularly interested in C++17.

I know that C++20 is the first version of the standard that enforces two's complement but the size of the variable is more important to me than the representation.

Yes, uintmax_t is guaranteed to be the unsigned counterpart of intmax_t .

From the C standard (N1570 7.20.1):

When typedef names differing only in the absence or presence of the initial u are defined, they shall denote corresponding signed and unsigned types as described in 6.2.5; an implementation providing one of these corresponding types shall also provide the other.

(C++ simply refers to C for the description of C standard library headers.)

6.2.5 p6:

For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned ) that uses the same amount of storage (including sign information) and has the same alignment requirements.

C++ is similar to C in this regard ( [basic.fundamental]/3 ):

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type [...] which occupies the same amount of storage and has the same alignment requirements as the corresponding signed integer type; that is, each signed integer type has the same object representation as its corresponding unsigned integer type. Likewise, for each of the extended signed integer types there exists a corresponding extended unsigned integer type with the same amount of storage and alignment requirements.

Which means intmax_t and uintmax_t always have the same size.

However, it is not guaranteed that the other two assertions will hold (prior to C++20/C23).

From the comments/title, it seems you're asking if they're required to be the same size; in which case, the short answer is yes... long answer... kind of:)

Quote from [basic.fundamental]/3 (C++17 draft N4713)

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: “unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”, each of which occupies the same amount of storage and has the same alignment requirements as its corresponding signed integer type.

(emphasis mine)

This guarantees that the unsigned versions take up the same size as their signed equivalents.

That being said, the standard [cstdint.syn] only states:

using intmax_t = signed integer type ;

using uintmax_t = unsigned integer type ;

The [basic.fundamental]/2 states

The standard and extended signed integer types are collectively called signed integer types .

and The [basic.fundamental]/3 states

The standard and extended unsigned integer types are collectively called unsigned integer types

So, technically, a compiler does not have to implement them as the same type, since that's an implementation detail; practically speaking though, they'd be the same.


As noted by duck, the C standard does indicate that there must be corresponding versions between types with u and no u prefix. The C standard is referenced via [cstdint.syn]/2

My answer was shown to be incorrect by duck. Thanks, duck!

https://stackoverflow.com/a/75203111/2027196

My original answer for future reference:

uintmax_t and intmax_t are not guaranteed by the standard to be the same width, but there is no system with a modern C compiler for which they will be different widths. There may not even be a non-modern C compiler for which they will be different widths ( asserted without evidence in the same vein as "the sky is blue" type arguments ).

The best you can get is that uintmax_t and intmax_t are guaranteed by convention to be the same width. I say "best you can get" but I'd be willing to rely on this guarantee more often than I rely on a compiler perfectly implementing all of its edge-case SFINAE requirements or similar.

Put your static_assert at the top of your library (maybe in an assumptions.hpp file) and then never worry about this problem ever again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM