2024-04-14
integration into IS ISO/IEC 9899:202y
document number | date | comment |
---|---|---|
n3241 | 202404 | original proposal |
CC BY, see https://creativecommons.org/licenses/by/4.0
The current integration of complex types into the C standard is a bit half-hearted:
there are no complex literals
basic operations such as computing real and imaginary part or the complex conjugate are delegated to functions that cannot be used in contexts where arithmetic constant expressions are required.
By combining _Generic
, casts to real types and complex arithmetic it is easy to provide expressions that result in the real, imaginary, complex conjugate and complex projective values, and that are suitable as arithmetic constant expressions. But these are not as easy to implement as it seems at the first sight, because they should properly take care of border cases such as imaginary parts that are positive or negative zeros, infinities or NaNs.
Several modern C compilers (gcc, clang, XL C) already have suffixes with i or j for complex literals since decades. In general, similar to the suffix component u for unsigned integer literals, i and j may precede or follow f and l and capitalized versions are also supported.
<complex>
Since users coming from different backgrounds might have different expectations, and since the compilers with these features already have the full combinatorics of these suffixes, we propose to integrate this convention into C2y as is.
The header <tgmath.h>
already provides type-generic macros for “complex-only” features, therefor we propose to improve those that don’t need C library support for a better integration into the language. In particular they should be suitable for arithmetic constant expressions.
Once having complex literals in the language, the macros creal
, cimag
, conj
and cproj
could actually be replaced by macros ℜ
, ℑ
, ℭ
and ℨ
as given below and that do not use C library calls, and such that the result is an arithmetic constant expressions whenever the argument is. Yet, the implementation has to be done carefully such that border cases for zeroes, infinities and NaNs are correct and correspond to the values and types that would be the result of the <complex.h>
functions.
#ifndef __STDC_NO_COMPLEX__
# define ℜ(Z) \
_Generic((Z), \
float: (float)(Z), \
long double: (long double)(Z), \
_Complex float: (float)(Z), \
_Complex long double: (long double)(Z), \
default: (double)(Z))
# define ℌ(Z) ((Z)*1.0IF) // left turn in the complex plane
# define ℑ(Z) \
_Generic((Z), \
_Complex float: -ℜ(ℌ(Z)), \
_Complex double: -ℜ(ℌ(Z)), \
_Complex long double: -ℜ(ℌ(Z)), \
float: 0.0F, \
long double: 0.0L, \
default: 0.0)
# define ℭ(Z) (ℜ(Z) + \
_Generic((Z), \
_Complex float: ℌ(ℜ(ℌ(Z))), \
_Complex double: ℌ(ℜ(ℌ(Z))), \
_Complex long double: ℌ(ℜ(ℌ(Z))), \
default: -0.0IF))
# define ℨ(Z) \
((isinf(ℜ(Z)) || isinf(ℑ(Z))) \
? (INFINITY + \
(signbit(ℑ(Z)) \
? _Generic(ℜ(Z), float: -0.0F, long double: -0.0L, -0.0) \
: _Generic(ℜ(Z), float: +0.0F, long double: +0.0L, +0.0))) \
: ℜ(Z) + ℑ(Z)*1.0IF)
#endif
Because such an implementation requires some knowledge about boundary cases of floating arithmetic and eventually about specific properties of implementations, we prefer that they’d provided by the C library in <tgmath.h>
by using existing interfaces creal
, cimag
, conj
and cproj
with reliable properties.
<tgmath.h>
as proposed in n3241 for C2y?Removals are in stroke-out red, additions in underlined green.
In p1, add the following suffixes to floating-suffix:
FI FJ I IF IL J JF JL LI LJ fi fj i if il j jf jl li lj
To Table 6.1 in p5 add the following lines
i, I, j, J double _Complex
if, IF, jf, JF, fi, FI, fj, FJ float _Complex
il, IL, jl, JL, li, LI, lj, LJ long double _Complex
Add to the same paragraph p5 at the end
Implementations that define the macro __STDC_NO_COMPLEX__
may not support suffixes that designate complex literals; the described floating value of such a complex literal designates the imaginary part of an arithmetic constant expression of the indicated complex type, the real part is (positive) zero.
Modify p1 as follows:
The header
<tgmath.h>
includes the headers<math.h>
and, if the conditional preprocessor expression__has_include(<complex.h>)
is true,<complex.h>
and defines several type-generic macros. If the conditional preprocessor expression__has_include(<complex.h>)
is false, the type-generic macros of this clause shall not access complex functions as defined in<complex.h>
. Nevertheless interfaces to functions in<math.h>
shall be unconditionally provided as described and additionally some complex macros if the implementation supports complex types.
Modify the second part of p10 as follows:
Use of
the macro with any argument of standard floating or complex type invokes a complex function. Use of the macroany of these macros with an argument of decimal floating type is undefined. Use of the macrocarg
with any argument of integer, standard floating or complex type invokes a complex function. If__STDC_NO_COMPLEX__
is not defined, the macroscreal
,cimag
,conj
andcproj
shall be (otherwise unconditioned) defined and shall result in a real floating type (forcreal
andcimag
) or complex floating type (forconj
andcproj
) corresponding to their argument. If the argument has a complex floating type the semantics are the same as for the corresponding<complex.h>
function. If the argument has a real floating type the result is
- the same as the converted argument (for
creal
),- a positive zero of the same type (for
cimag
),- a conversion to the corresponding complex type with a negative zero as imaginary part (for
conj
) and- a conversion to the corresponding complex type mapping minus infinity to plus infinity and with a positive zero as imaginary part (for
cproj
), respectively.
If the argument has integer type, it is first converted to double
. In all cases, a possible excess precision of the argument value is truncated as if by a cast operation and if the argument is an arithmetic constant expression the result also has that property.