2025-04-29
integration into IS ISO/IEC 9899:202y
document number | date | comment |
---|---|---|
n3543 | 202504 | this document |
Generic features to compute minimum and maximum value of two given values is notoriously challenging in C.
fmin
tg macro of <tgmath.h>
)
may result in loss of precision.We propose to add type-generic macros that solve all these problems. Namely:
In particular the choice for the minimum differs from the usual
arithmetic conversions. If one type is signed and the other is unsigned
the macro ckd_min
chooses the signed
type for the result.
Possible problems arise
For these cases we propose different variants, one that rejects translation in these cases, one that makes these cases implementation-defined, including the possibility to reject translation, and one that has undefined behavior.
For convenience, we propose to add the features to the <stdckdint.h>
header with the idea this header contains operations that are merely
language features that always have defined behavior. The suggested
wording could easily be adapted to be placed in a different header
(clause 7) or even to be introduced as proper operators (clause 6).
Implementations of minimum and maximum functions there are plenty. Even the current C standard provides a handful of slightly different interfaces, even for type-generic ones. Then, in the field an abundant number of implementations of varying properties and quality are added to that picture.
We are not aware of an implementation of these functionalities that
all the problems that are listed in the introduction. In particular,
none of them seems to provide an integer constant expression where this
would be possible. This is all the more surprising as that property has
a clear demand (in particular for array lengths) and as providing macros
that have that property is not too difficult by using either
implementation-specific extensions (such as gcc’s __builtin_constant_p
) and/or playing tricks
with _Generic
.
The goal of this paper is to propose a unification to all these interfaces and their behavior, such that programmers find reliable implementations of these features in their C library. The reference implementation that we maintain (and are able to provide on request) is not meant to suggest any particular way in which these features should be implemented, but only to prove that an implementation is possible as of today with minimal effort.
In 7.20.1, bump the value of __STDC_VERSION_STDCKDINT_H__
.
Then add a new clause
7.20.4 Minimum and maximum operation type-generic macros
Synopsis
#include <stdckdint.h>
(typeA a, typeB b);
minType ckd_min(typeA a, typeB b); maxType ckd_max
Description
2 These type-generic macros compute the minimum and maximum value of their arguments, respectively. If both arguments are integer constant expressions, the macro call expression is also an integer constant expressions.
3 The result typemaxType
istypeof(a+b)
. The result typeminType
is determined as follows:
- If
maxType
is an unsigned integer type and if one oftypeA
ortypeB
has negative values,minType
istypeof(+a)
ortypeof(+b)
, respectively.
- Otherwise,
minType
is the same asmaxType
.
Returns
4 If none of the arguments is a NaN, the result of the operation is the mathematically lesser (respectively greater) argument converted to typeminType
andmaxType
, respectively. Otherwise, if one of the arguments is a signaling NaN, the “domain error” floating point exception is raised and the result is a signaling NaN of the appropriate type. Otherwise, if one of the arguments is a quiet NaN, no floating point exception is raised and the result is a quiet NaN of the appropriate type.
5 NOTE 1 If both arguments have integer type,
minType
andmaxType
are able to represent the mathematical result of the respective operation.
6 NOTE 2 For other type combinations (e.g combining auint64_t
integer and an ISO/IEC 60559 binary64 floating point type) the typesminType
andmaxType
(here the floating point type) is not able to hold the precise result of the operations for all possible combinations of argument values.
7 Example
#include <ckdint.h>
constexpr size_t n = SIZE_MAX;
static double A[ckd_max(n, 1)]; // valid
constexpr auto ms = ckd_min(n, 0); // valid, type is int
constexpr auto mu = ckd_min(n, 0u); // valid, type is size_t
constexpr auto mc = ckd_min(n, (char)0); // valid, type depends on architecture
constexpr auto md = ckd_min(n, 0.0); // type double, possibly invalid
Here, the array length expression ofA
computes the maximum of two integer constant expressions and is thus an integer constant expression, too. Forms
, one of the arguments tockd_min
has a signed type, so the result type is that signed type. Conversely, formu
both arguments have an unsigned type, and so the result is the common real type. The type ofmc
depends on whether or notchar
admits negative values; if so, the result type isint
, otherwise it issize_t
. Formd
the common real type isdouble
, but it is implementation-defined if the value ofSIZE_MAX
is representable without loss of precision in that type.
It is important to add the possibility to reject combinations that cannot properly represent the mathematical result in all cases. Therefore one of the following variants should be added to the text above.
Add to 7.20.4 as described above
3′ IftypeA
andtypeB
do not have a common real type or if the result type of the operation is not able to hold the result for all possible value combinations oftypeA
andtypeB
, the program translation fails.
Add to the end of 7.20.4 p7 (example) as described above
If it is, all possible value combinations of values are representable in
the result and the definition of md
is
accepted; otherwise the program translation fails.
Add to J .3.16, architecture specific behavior
- If calls to the
ckd_min
andckd_max
type-generic macros with certain combinations of integer and floating arguments are accepted (7.20.4).
Add to 7.20.4 as described above
3′ IftypeA
andtypeB
do not have a common real type it is implementation-defined if the program translation fails or if the typesminType
andmaxType
are chosen in an implementation-defined way. If the result type of the operation is not able to hold the result for all possible value combinations oftypeA
andtypeB
, it is implementation-defined if the operation is accepted, possibly resulting in a loss of precision, or if the program translation fails.
Add to the end of 7.20.4 p7 (example) as described above
If it is, the definition ofmd
is accepted. Otherwise it is implementation-defined if the program translation fails or ifmd
has the value(double)SIZE_MAX
.
Add to J .3.15, implementation-defined behavior
- The behavior of calls to the
ckd_min
andckd_max
type-generic macros if their arguments do not have a common real type or if the result type cannot represent all possible results (7.20.4).
Add to 7.20.4 as described above
3′ IftypeA
andtypeB
do not have a common real type or if the result type of the operation is not able to hold the result for all possible value combinations oftypeA
andtypeB
, the behavior is undefined.
Add to the end of 7.20.4 p7 (example) as described above
If it is, the definition of md
is
valid, otherwise the behavior is undefined.
Add to J .2, undefined behavior
- The behavior of calls to the
ckd_min
andckd_max
type-generic macros if their arguments do not have a common real type or if the result type cannot represent all possible results (7.20.4).