Introduce complex literals

Jens Gustedt, INRIA and ICube, France

2024-04-14

target

integration into IS ISO/IEC 9899:202y

document history

document number date comment
n3241 202404 original proposal

license

CC BY, see https://creativecommons.org/licenses/by/4.0

1 Problem description

The current integration of complex types into the C standard is a bit half-hearted:

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.

2 Proposed additions to C2y

2.1 Complex literals

2.1.1 C compilers

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.

2.1.2 Other languages

2.1.3 Proposal

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.

2.2 Type-generic complex operators

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.

3 Questions

  1. Does WG14 want to integrate complex number literals as proposed in n3241 into C2y?
  2. Does WG14 want to amend <tgmath.h> as proposed in n3241 for C2y?

4 Wording

Removals are in stroke-out red, additions in underlined green.

4.1 Changes to clause 6.4.4.3

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.

4.2 Changes in clause 7.27 (Type-generic math)

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 macro any of these macros with an argument of decimal floating type is undefined. Use of the macro carg with any argument of integer, standard floating or complex type invokes a complex function. If __STDC_NO_COMPLEX__ is not defined, the macros creal, cimag, conj and cproj shall be (otherwise unconditioned) defined and shall result in a real floating type (for creal and cimag) or complex floating type (for conj and cproj) 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

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.