Complex arithmetic semantics

Annex G of ISO/IEC 9899 specifies what *, /, + and - mean when their operands are real, imaginary or complex, and what the <complex.h> functions return at every infinity, zero and NaN. It is normative only for implementations that define __STDC_IEC_559_COMPLEX__, and it is the only place in C where a type rule, an exception flag and the sign of a zero all decide the answer together. This page reads G.5 to G.7 as a working reference, with every table rendered as a grid so the shape of the special cases is visible before the individual values are.

3Arithmetic types: real, imaginary, complex
114Special cases enumerated in G.6
10Functions with a special-value catalogue
4Infinity properties binding on * and /

Counts are of the clauses in G.6.1 to G.6.4, excluding the symmetry identity that opens each list. Source: ISO/IEC 9899:2017 (C17), Annex G.

Three types, one plane

Where this sits: the C article covers the standard lineage that made these types optional in C11, and the reference page lists the headers involved. This page is about what Annex G actually requires of the arithmetic.

C99 gave the language two families of floating types where Fortran and C++ have one. Alongside the real types there are the complex types, and — only in an Annex G implementation — the imaginary types. An imaginary value is a single real number understood as a coefficient of i; it is not a complex number whose real part happens to be zero, and the difference is not cosmetic. 0.0 + iy and iy occupy the same point of the plane but do not behave the same way under multiplication, because one of them has a real part that participates in every partial product and the other has no real part at all.

The imaginary types are optional even where Annex G applies: an implementation signals them with __STDC_IEC_559_COMPLEX__, and portable code that wants the effects described here must be prepared for a translation unit where _Imaginary does not exist. The complex types themselves became optional in C11, guarded by __STDC_NO_COMPLEX__.

Type familySpellingRepresentationWhat it contributes to an operation
Realfloat, double, long doubleOne valueA single coefficient; no imaginary part to cross-multiply
Imaginaryfloat _Imaginary and friends, via imaginaryOne value, read as the coefficient of iA single coefficient, plus the sign flip that i2 = −1 forces
Complexfloat _Complex and friends, via complexTwo values, real part then imaginary partBoth coefficients, so every product of two complex values has four partial products

The three arithmetic type families of an Annex G implementation. Source: ISO/IEC 9899:2017, 6.2.5 and G.2.

Back to top

The type algebra of the operators

The multiplicative and additive operators disagree about which combination of operand types stays out of the complex types, and the disagreement is the whole reason imaginary types earn their keep. Under * and /, imaginary is closed with real: two imaginary operands give a real result and a mixed pair gives an imaginary one, so a chain of scalings never widens to two components. Under + and - it is the mixed pair that widens, because a real part and an imaginary part added together are exactly what a complex number is.

real float, double, long doubleimaginary _Imaginary, Annex G onlycomplex _Complex
* and /realimaginarycomplexrealreal · real — real * real is realrealreal · imaginary — G.5.1 p1: one real operand, one imaginary operand, imaginary resultimaginaryreal · complex — G.5.1 p1: either operand complex, complex resultcompleximaginaryimaginary · real — G.5.1 p1: one real operand, one imaginary operand, imaginary resultimaginaryimaginary · imaginary — G.5.1 p1: both operands imaginary, real resultrealimaginary · complex — G.5.1 p1: either operand complex, complex resultcomplexcomplexcomplex · real — G.5.1 p1: either operand complex, complex resultcomplexcomplex · imaginary — G.5.1 p1: either operand complex, complex resultcomplexcomplex · complex — G.5.1 p1: either operand complex, complex resultcomplex
Figure 1. Result type of * and /, by operand type. The imaginary row and column stay out of the complex types entirely: only an operand that is already complex produces one. Source: ISO/IEC 9899:2017, G.5.1 paragraph 1.
+ and -realimaginarycomplexrealreal · real — real + real is realrealreal · imaginary — G.5.2 p1: one real operand, one imaginary operand, complex resultcomplexreal · complex — G.5.2 p1: either operand complex, complex resultcompleximaginaryimaginary · real — G.5.2 p1: one real operand, one imaginary operand, complex resultcompleximaginary · imaginary — G.5.2 p1: both operands imaginary, imaginary resultimaginaryimaginary · complex — G.5.2 p1: either operand complex, complex resultcomplexcomplexcomplex · real — G.5.2 p1: either operand complex, complex resultcomplexcomplex · imaginary — G.5.2 p1: either operand complex, complex resultcomplexcomplex · complex — G.5.2 p1: either operand complex, complex resultcomplex
Figure 2. Result type of + and -, by operand type. Here the mixed pair is the one that widens, and it is the only rule that differs from Figure 1. Source: ISO/IEC 9899:2017, G.5.2 paragraph 1.

Read the two grids against each other rather than in turn. The four corners agree; the two off-diagonal cells swap; and the middle cell — imaginary against imaginary — is real under multiplication and imaginary under addition. Every practical consequence of having imaginary types in the language follows from those three differences.

Back to top

The operator formula tables

For every combination except complex against complex, G.5.1 and G.5.2 do not describe the result approximately: they give the exact expression the implementation must evaluate, and the floating-point exception behaviour is whatever that expression raises. Nothing is left to the implementation, and in particular nothing may be computed by promoting the operands to complex and running the general formula — that would introduce products with a zero part, and products with a zero part are exactly how an infinity turns into a NaN.

real float, double, long doubleimaginary _Imaginary, Annex G onlycomplex _Complex
z * wuivu + ivxx · u — x * u is the ordinary real productxux · iv — a real operand scales an imaginary one and the result stays imaginaryi(xv)x · u + iv — one real operand scales both parts: two real multiplications(xu) + i(xv)iyiy · u — an imaginary operand scaled by a real one stays imaginaryi(yu)iy · iv — i squared is minus one, so two imaginary operands give a negated real product(−y)viy · u + iv — the imaginary operand crosses into both parts((−y)v) + i(yu)x + iyx + iy · u — a complex operand scaled by a real one: two real multiplications(xu) + i(yu)x + iy · iv — a complex operand scaled by an imaginary one: the parts swap and one is negated((−y)v) + i(xv)not specified
Figure 3. The multiplication table of G.5.1 paragraph 2, with each cell coloured by the type of its result. The bottom-right cell is empty in the standard as well as here: when both operands are complex, paragraph 2 does not apply and paragraph 5 hands the calculation to the implementation, subject to the infinity properties in paragraph 4. Source: ISO/IEC 9899:2017, G.5.1 paragraph 2.
z / wuivxx · u — an ordinary real quotientx/ux · iv — dividing a real by an imaginary is a quarter turn, hence the negationi((−x)/v)iyiy · u — an imaginary numerator over a real denominator stays imaginaryi(y/u)iy · iv — imaginary over imaginary is real, with no negationy/vx + iyx + iy · u — a complex numerator over a real denominator: two real divisions(x/u) + i(y/u)x + iy · iv — a complex numerator over an imaginary denominator: parts swap, one negates(y/v) + i((−x)/v)
Figure 4. The division table of G.5.1 paragraph 3. It has two columns rather than three, because the paragraph applies only where the second operand is not complex; a complex divisor is again left to the implementation. Note the negation in the top-right and bottom-right cells: dividing by an imaginary value is a quarter turn, and a quarter turn moves a sign. Source: ISO/IEC 9899:2017, G.5.1 paragraph 3.
z ± wuivu + ivxx · u — two real operands: an ordinary real sumx±ux · iv — a real and an imaginary operand assemble with no arithmetic at allx ± ivx · u + iv — the real operand meets only the real part(x±u) ± iviyiy · u — a real and an imaginary operand assemble with no arithmetic at all±u + iyiy · iv — two imaginary operands stay imaginaryi(y±v)iy · u + iv — the imaginary operand meets only the imaginary part±u + i(y±v)x + iyx + iy · u — the real operand meets only the real part(x±u) + iyx + iy · iv — the imaginary operand meets only the imaginary partx + i(y±v)x + iy · u + iv — componentwise, and always exact if the components are(x±u) + i(y±v)
Figure 5. The additive table of G.5.2 paragraph 2, which unlike the other two is complete: every combination of operand types is covered, because addition never needs a cross term. The four cells on the mixed diagonal do no arithmetic at all — they assemble a complex value out of parts that are already there. Source: ISO/IEC 9899:2017, G.5.2 paragraph 2.

The plus-or-minus in Figure 5 is the operator itself, not an unspecified sign: the same table serves + and -, and a leading ±u means the real part is u for addition and -u for subtraction.

Back to top

What the tables cost in real operations

The formula tables are not only a semantic specification; they are a cost model. Every cell names the exact real operations the implementation performs, so the price of losing an imaginary type to an implicit conversion is countable. A rotation written as z * I with an imaginary I is two moves and a sign flip. The same rotation written against a complex constant is four multiplications, two additions, and a NaN-recovery branch that has to run whenever both parts of the result come out NaN.

x * u — 1. 1 multiply; real resultx * u1x * iv — 1. 1 multiply; imaginary resultx * iv1iy * u — 1. 1 multiply; imaginary resultiy * u1iy * iv — 1. 1 multiply and a negation; real resultiy * iv1x * (u+iv) — 2. 2 multiplies; complex resultx * (u+iv)2iy * (u+iv) — 2. 2 multiplies; complex resultiy * (u+iv)2(x+iy) * u — 2. 2 multiplies; complex result(x+iy) * u2(x+iy) * iv — 2. 2 multiplies; complex result(x+iy) * iv2(x+iy) * (u+iv) — 4. 4 multiplies and 2 adds, before any NaN recovery(x+iy) * (u+iv)404
Figure 6. Real multiplications per multiplication, counted from the cells of Figure 3. The four single-multiply rows are the ones an imaginary type makes reachable; the four-multiply row at the bottom is what every one of them becomes if the operand widens to complex first. Source: derived from ISO/IEC 9899:2017, G.5.1 paragraph 2.
x / u — 1. 1 dividex / u1x / iv — 1. 1 divide and a negationx / iv1iy / u — 1. 1 divideiy / u1iy / iv — 1. 1 divideiy / iv1(x+iy) / u — 2. 2 divides(x+iy) / u2(x+iy) / iv — 2. 2 divides and a negation(x+iy) / iv2(x+iy) / (u+iv) — 11. 6 multiplies, 3 adds, 2 divides, plus logb and two scalbn(x+iy) / (u+iv)11011
Figure 7. Real operations per division, counted the same way, with the complex-by-complex case taken from the reference implementation in G.5.1 Example 2 rather than from the formula table, which does not cover it. The gap between two operations and eleven is the reason that case gets its own scaled algorithm. Source: derived from ISO/IEC 9899:2017, G.5.1 paragraph 3 and Example 2.

Counts are of arithmetic operations named in the formula, not of instructions: a fused multiply-add may merge two of them, which is why both worked examples in the annex open with #pragma STDC FP_CONTRACT OFF.

Back to top

The infinity properties

Paragraph 4 of G.5.1 states four properties that hold for all real, imaginary and complex operands — including the complex-by-complex case the formula tables decline to cover. Footnote 383 is explicit that this is not a restatement of the tables: the tables already imply these results where they apply, and paragraph 4 extends them to every case the tables leave open. In practice this is the contract that forces the recovery code in the next section to exist.

Multiplication is symmetric here, so the first row covers both orders of operand. Division is not, and the three division rows are three genuinely different rules.

OperatorFirst operandSecond operandResult
*an infinitya nonzero finite number or an infinityan infinity
/an infinitya finite numberan infinity
/a finite numberan infinitya zero
/a nonzero finite number or an infinitya zeroan infinity

The four infinity properties. “An infinity” means a value with at least one infinite part, which by G.3 includes a value whose other part is a NaN. Source: ISO/IEC 9899:2017, G.5.1 paragraph 4.

Finite every part finite and not both zeroZero both parts are zeros, signs as writtenInfinity at least one part infinite — an infinity per G.3, even when the other part is a NaNNaN a NaN part with no infinite part
z * w±0finite ≠ 0NaN±0not specifiednot specifiednot specifiednot specifiedfinite ≠ 0not specifiednot specifiedfinite ≠ 0 · ∞ — an infinity times a nonzero finite number is an infinityG.5.1 ¶4not specifiednot specified∞ · finite ≠ 0 — an infinity times a nonzero finite number is an infinityG.5.1 ¶4∞ · ∞ — an infinity times an infinity is an infinityG.5.1 ¶4not specifiedNaNnot specifiednot specifiednot specifiednot specified
Figure 8. Which cells of the multiplication operator paragraph 4 pins, whatever the operand types. The three pinned cells are the only ones; everything else is left to the formula tables or to the implementation. Source: ISO/IEC 9899:2017, G.5.1 paragraph 4.
z / w±0finite ≠ 0±0not specifiednot specified±0 · ∞ — a finite first operand over an infinite second operand is a zero0G.5.1 ¶4finite ≠ 0finite ≠ 0 · ±0 — a nonzero finite first operand over a zero second operand is an infinityG.5.1 ¶4not specifiedfinite ≠ 0 · ∞ — a finite first operand over an infinite second operand is a zero0G.5.1 ¶4∞ · ±0 — an infinite first operand over a zero, which is a finite number, is an infinityG.5.1 ¶4∞ · finite ≠ 0 — an infinite first operand over a finite second operand is an infinityG.5.1 ¶4not specified
Figure 9. The same for division, with the numerator on the rows and the denominator on the columns. Division pins five cells rather than three, and the two zero results are the only place in the annex where an operator is required to underflow all the way to a zero. Source: ISO/IEC 9899:2017, G.5.1 paragraph 4.

Neither grid pins a single cell in a NaN row or column, and that is deliberate rather than an omission: paragraph 4 is about infinities, and a NaN operand is governed by the ordinary IEC 60559 rules for the real operations the formula tables name. The gap is where the trouble starts, because the ordinary rules turn ∞ × 0 into a NaN and there is no complex-level rule that puts the infinity back.

Back to top

Multiplication: recovering the infinities

Take z = ∞ + i∞ and w = 2 + i0. Both are perfectly good values, the product is unambiguously an infinity, and the textbook formula computes it as a NaN in both parts. The reason is that ∞ × 0 is a NaN under IEC 60559 and two of the four partial products contain one. Once a NaN enters a partial product it survives the subtraction and the addition, and the infinity that was there in the operand is gone.

a·c, b·d, a·d, b·cacbdadbcxynaivenaive · ac — ac is infinity times two, an infinity+∞∞ × 2naive · bd — bd is infinity times zero, a NaN under IEC 60559NaN∞ × 0naive · ad — ad is infinity times zero, a NaN under IEC 60559NaN∞ × 0naive · bc — bc is infinity times two, an infinity+∞∞ × 2naive · x — the real part of the result is a NaNNaN∞ − NaNnaive · y — the imaginary part of the result is a NaNNaNNaN + ∞after boxingafter boxing · ac — after boxing, a is one and c is two+21 × 2after boxing · bd — after boxing, b is one and d is zero+01 × 0after boxing · ad — after boxing, a is one and d is zero+01 × 0after boxing · bc — after boxing, b is one and c is two+21 × 2after boxing · x — the real part is recovered as an infinity+∞∞ × 2after boxing · y — the imaginary part is recovered as an infinity+∞∞ × 2
Figure 10. One multiplication, twice: the four partial products and the two result parts for (∞ + i∞) × (2 + i0), computed by the textbook formula and then by the recovery path. The last two columns are the result parts, x = ac − bd and y = ad + bc. Boxing replaces each infinite operand part with a signed one and each of its NaN counterparts with a signed zero, which removes every ∞ × 0 from the products without changing the sign of anything. Source: worked from ISO/IEC 9899:2017, G.5.1 Example 1.

The reference implementation

G.5.1 Example 1 gives an implementation of double _Complex multiplication that meets the paragraph 4 contract. Its shape is worth reading closely: the recovery path is guarded by a single isnan(x) && isnan(y) test. && short-circuits and an ordinary product has a non-NaN real part, so the finite path pays for one isnan and nothing else — which is exactly the cost the annex claims for it.

#include <math.h>
#include <complex.h>

/* Multiply z * w ... */
double complex _Cmultd(double complex z, double complex w)
{
    #pragma STDC FP_CONTRACT OFF
    double a, b, c, d, ac, bd, ad, bc, x, y;

    a = creal(z); b = cimag(z);
    c = creal(w); d = cimag(w);
    ac = a * c; bd = b * d;
    ad = a * d; bc = b * c;
    x = ac - bd; y = ad + bc;

    if (isnan(x) && isnan(y)) {
        /* Recover infinities that computed as NaN + iNaN ... */
        int recalc = 0;
        if (isinf(a) || isinf(b)) {              /* z is infinite */
            /* "Box" the infinity and change NaNs in the other factor to 0 */
            a = copysign(isinf(a) ? 1.0 : 0.0, a);
            b = copysign(isinf(b) ? 1.0 : 0.0, b);
            if (isnan(c)) c = copysign(0.0, c);
            if (isnan(d)) d = copysign(0.0, d);
            recalc = 1;
        }
        if (isinf(c) || isinf(d)) {              /* w is infinite */
            c = copysign(isinf(c) ? 1.0 : 0.0, c);
            d = copysign(isinf(d) ? 1.0 : 0.0, d);
            if (isnan(a)) a = copysign(0.0, a);
            if (isnan(b)) b = copysign(0.0, b);
            recalc = 1;
        }
        if (!recalc && (isinf(ac) || isinf(bd) ||
                        isinf(ad) || isinf(bc))) {
            /* Recover infinities from overflow by changing NaNs to 0 ... */
            if (isnan(a)) a = copysign(0.0, a);
            if (isnan(b)) b = copysign(0.0, b);
            if (isnan(c)) c = copysign(0.0, c);
            if (isnan(d)) d = copysign(0.0, d);
            recalc = 1;
        }
        if (recalc) {
            x = INFINITY * (a * c - b * d);
            y = INFINITY * (a * d + b * c);
        }
    }
    return x + I * y;
}

Source: ISO/IEC 9899:2017, G.5.1 Example 1, reformatted. The annex notes that this achieves the required treatment of infinities at the cost of one isnan test in ordinary finite cases, and that it is less than ideal in that undue overflow and underflow may occur.

four productsboth parts NaN?box operandsrecalc with ∞x, yyesrecalc = 1no: return x + iy unchanged
Figure 11. The control flow of Example 1. Three conditions can set recalc, and they are checked in an order that matters: an infinite operand is boxed in preference to an overflowed partial product, and the third test runs only when neither operand was itself infinite. Source: ISO/IEC 9899:2017, G.5.1 Example 1.
ConditionWhat it boxesWhy it is needed
isinf(a) || isinf(b)a, b to a signed 1 or 0; NaNs in c, d to a signed 0z is an infinity, so paragraph 4 requires an infinite result whenever w is nonzero
isinf(c) || isinf(d)c, d the same way; NaNs in a, b to a signed 0the symmetric case; both may fire, when both operands are infinities
!recalc && (isinf(ac) || isinf(bd) || isinf(ad) || isinf(bc))every NaN operand part to a signed 0neither operand was infinite, but a partial product overflowed to one; the result is still required to be an infinity

The three recovery conditions of Example 1. copysign does the boxing, so the magnitude collapses to 1 or 0 while every sign survives — which is what lets the final INFINITY * reconstruct the correct quadrant. Source: ISO/IEC 9899:2017, G.5.1 Example 1.

Back to top

Division: scaling the denominator

Complex division has the same NaN problem as multiplication and one more of its own. The denominator c*c + d*d squares the operand, so it overflows whenever either part of the divisor exceeds the square root of the largest finite value, and flushes to zero whenever both parts fall below the square root of the smallest positive value. For double that means a divisor with a part above roughly 1.3 × 10154 produces an infinite denominator and a zero quotient, and a divisor with both parts below roughly 2 × 10-162 produces a zero denominator and an infinite quotient — in both cases from operands that were nowhere near the edge of the format.

2^-10742^-5492^-252^5002^1024representable divisor — 2^-1074 uprepresentable divisor2^-1074 updenominator overflows — 2^512 updenominator overflows2^512 updenominator flushes to zero — 2^-537 downdenominator flushes to zero2^-537 downsafe without scaling — 2^-537 to 2^512safe without scaling2^-537 to 2^512after scalbn(c, -ilogbw) — exponent 0 alwaysafter scalbn(c, -ilogbw)exponent 0 always
Figure 12. Where the unscaled denominator fails, over the whole exponent range of double. Roughly half the representable exponents are unsafe for the textbook formula, and scaling collapses every one of them onto the single exponent at the middle of the safe band. The axis is the binary exponent of max(|c|, |d|), to scale. Source: derived from IEC 60559 binary64 limits and ISO/IEC 9899:2017, G.5.1 Example 2.

Example 2 fixes this by scaling. It takes the binary exponent of the larger divisor part with logb, divides both parts by that power of two with scalbn, does the division on the scaled values, and scales the two result parts back. Because both scalings are by powers of two they are exact, which is why the annex notes that scaling with scalbn rather than with a division gives better roundoff characteristics. The numerator is left unscaled, and the annex says so plainly: this code does not defend against overflow and underflow there either.

logbexponent of max|c|,|d|ilogbwscalbndivisor to [1, 2)c, ddividedenom = c² + d²x, yscalbnquotient backif NaNrecoverNaN + iNaN only
Figure 13. The pipeline of Example 2. Only the middle stage is the division itself; the two stages around it exist to keep the denominator in a range where squaring cannot overflow, and the last one exists because that range is not where the answer lives. Source: ISO/IEC 9899:2017, G.5.1 Example 2.
#include <math.h>
#include <complex.h>

/* Divide z / w ... */
double complex _Cdivd(double complex z, double complex w)
{
    #pragma STDC FP_CONTRACT OFF
    double a, b, c, d, logbw, denom, x, y;
    int ilogbw = 0;

    a = creal(z); b = cimag(z);
    c = creal(w); d = cimag(w);

    logbw = logb(fmax(fabs(c), fabs(d)));
    if (isfinite(logbw)) {
        ilogbw = (int)logbw;
        c = scalbn(c, -ilogbw); d = scalbn(d, -ilogbw);
    }
    denom = c * c + d * d;
    x = scalbn((a * c + b * d) / denom, -ilogbw);
    y = scalbn((b * c - a * d) / denom, -ilogbw);

    /* Recover infinities and zeros that computed as NaN + iNaN;   */
    /* the only cases are nonzero/zero, infinite/finite, and        */
    /* finite/infinite ...                                          */
    if (isnan(x) && isnan(y)) {
        if ((denom == 0.0) && (!isnan(a) || !isnan(b))) {
            x = copysign(INFINITY, c) * a;
            y = copysign(INFINITY, c) * b;
        }
        else if ((isinf(a) || isinf(b)) && isfinite(c) && isfinite(d)) {
            a = copysign(isinf(a) ? 1.0 : 0.0, a);
            b = copysign(isinf(b) ? 1.0 : 0.0, b);
            x = INFINITY * (a * c + b * d);
            y = INFINITY * (b * c - a * d);
        }
        else if ((logbw == INFINITY) && isfinite(a) && isfinite(b)) {
            c = copysign(isinf(c) ? 1.0 : 0.0, c);
            d = copysign(isinf(d) ? 1.0 : 0.0, d);
            x = 0.0 * (a * c + b * d);
            y = 0.0 * (b * c - a * d);
        }
    }
    return x + I * y;
}

Source: ISO/IEC 9899:2017, G.5.1 Example 2, reformatted.

Recovery branchThe case it catchesWhich infinity property it satisfies
denom == 0.0 && (!isnan(a) || !isnan(b))nonzero over zeroa nonzero finite or infinite numerator over a zero is an infinity
(isinf(a) || isinf(b)) && isfinite(c) && isfinite(d)infinite over finitean infinite numerator over a finite denominator is an infinity
logbw == INFINITY && isfinite(a) && isfinite(b)finite over infinitea finite numerator over an infinite denominator is a zero

The three recovery branches, one per division property in G.5.1 paragraph 4. The comment in the annex is exact: these are the only cases that reach the branch, because every other route to NaN + iNaN is a genuine NaN. Source: ISO/IEC 9899:2017, G.5.1 Example 2.

Note that logbw, not c or d, is what the third branch tests. logb of an infinity is an infinity, so a single comparison identifies an infinite divisor without inspecting either part — and it also explains why the scaling is guarded by isfinite(logbw) in the first place.

Back to top

CX_LIMITED_RANGE and FP_CONTRACT

Everything above is what the implementation owes you by default. Two pragmas let you give some of it back, and both worked examples in the annex begin by switching one of them off.

Both are block-scoped and both revert with DEFAULT. Turning CX_LIMITED_RANGE on is a licence, not an instruction: the implementation may ignore it entirely.

PragmaDefaultON permitsWhy you would
#pragma STDC CX_LIMITED_RANGE ONOFFthe usual mathematical formulas for complex multiplication, division and absolute value, without the infinity and overflow safeguardsthe four-multiply formula with no NaN test, where the operands are known to be moderate and the branch is measurable
#pragma STDC FP_CONTRACT OFFimplementation-definednothing — it forbids contracting an expression into a single operationa fused multiply-add would evaluate a*c - b*d with one rounding instead of two, which changes which cases come out as an exact zero

The two pragmas that govern how literally the formula tables are followed. Footnote 383 attaches the infinity properties to the state where CX_LIMITED_RANGE is off. Sources: ISO/IEC 9899:2017, 6.5 and 7.3.4, and G.5.1 footnote 383.

Turning CX_LIMITED_RANGE on discards the infinity properties of G.5.1 paragraph 4 along with the cost of enforcing them. A product of two infinities may then be NaN + iNaN, and code that tests isinf(creal(z)) || isinf(cimag(z)) to detect overflow will stop detecting it.

Back to top

G.6 changes one macro definition, and the change is the practical payoff of the whole annex. Clause 7.3 defines I as _Complex_I, a complex constant whose real part is a zero. G.6 redefines it as _Imaginary_I, an imaginary constant with no real part at all. Notwithstanding 7.1.3, a program may undefine and then redefine imaginary; I itself is the macro that quietly decides whether x + I*y is correct.

Finite every part finite and not both zeroZero both parts are zeros, signs as writtenInfinity at least one part infinite — an infinity per G.3, even when the other part is a NaNNaN a NaN part with no infinite part
expressionI is _Complex_I (7.3)I is _Imaginary_I (G.6)I * yI * y · I is _Complex_I (7.3) — a complex times a real is two real multiplications(0·y) + i(1·y)two productsI * y · I is _Imaginary_I (G.6) — an imaginary times a real is one real multiplicationi(1·y)one productI * INFINITYI * INFINITY · I is _Complex_I (7.3) — the zero real part of the complex I meets the infinityNaN + i∞0 × ∞ is a NaNI * INFINITY · I is _Imaginary_I (G.6) — there is no real part to meet the infinity, so no NaN appearsi∞exactx + I*y, y = ∞x + I*y, y = ∞ · I is _Complex_I (7.3) — the spurious NaN propagates into the real part of the sumNaN + i∞real part lostx + I*y, y = ∞ · I is _Imaginary_I (G.6) — the parts are assembled, per the additive table, with no arithmeticx + i∞exact
Figure 14. The same three expressions under the two definitions of I. The third row is the one that matters: a complex I carries a zero real part into every product, and a zero times an infinity is a NaN, so the real part of the result is destroyed by a multiplication that mathematically does nothing. Source: ISO/IEC 9899:2017, 7.3 and G.6 paragraph 1.

This is why C11 added the CMPLX, CMPLXF and CMPLXL macros to <complex.h>. On an implementation without imaginary types there is no way to write a complex value with a given pair of parts using arithmetic alone, because x + I*y goes through the complex I and loses infinities and signed zeros. CMPLX(x, y) is defined to build the value directly, and it is the portable spelling.

/* Portable on any C11 implementation, whether or not it has imaginary types. */
double complex z = CMPLX(1.0, INFINITY);        /* 1.0 + i INF, exactly     */

/* Correct only where I is _Imaginary_I, that is, under Annex G. */
double complex w = 1.0 + I * INFINITY;          /* NaN + i INF otherwise    */

/* Detecting the two feature macros. */
#ifdef __STDC_NO_COMPLEX__
#  error "this translation unit needs the complex types"
#endif
#ifdef __STDC_IEC_559_COMPLEX__
   /* Annex G applies: imaginary types exist and the tables above are binding. */
#endif

Sources: ISO/IEC 9899:2017, 6.10.8.3 (conditional feature macros), 7.3.9.3 (the CMPLX macros), and G.6 paragraph 1.

Back to top

Branch cuts and the sign of zero

Every inverse of a periodic or multivalued function needs a cut in the plane where the principal value jumps. G.6 paragraph 3 states the rule that makes those jumps usable rather than merely survivable: the functions are continuous onto both sides of their branch cuts, taking into account the sign of zero. A signed zero in the imaginary part is not a rounding artefact to be normalised away — it is which side of the cut you are approaching from, and the annex gives csqrt(−2 ± i0) = ±i√2 as the worked case.

This is the practical reason a complex library must never normalise -0.0 to 0.0. Two values that compare equal under == sit on opposite sides of a cut and produce results that differ in sign. It is also why the additive table in Figure 5 matters: assembling x + i(-0.0) componentwise preserves the sign, and computing it as x + I*(-0.0) with a complex I does not.

-2-2i-1-1i11i22iReImBranch cut — clog and csqrt: the non-positive real axis−2 + i0 → +i√2−2 + i0 → +i√2−2 − i0 → −i√2−2 − i0 → −i√2
Figure 15. The cut shared by clog and csqrt: the non-positive real axis. The two marked points are the same real number approached from the two sides, and the annex pins their square roots to opposite signs. Source: ISO/IEC 9899:2017, G.6 paragraph 3 and G.6.4.2.
-2-2i-1-1i11i22iReImBranch cut — casin, cacos, catanh: below −1Branch cut — casin, cacos, catanh: above +1+1: pole+1: pole−1: pole−1: pole
Figure 16. The cuts of casin, cacos and catanh: the real axis outside the closed interval from −1 to 1. catanh also has a pole at each endpoint, where G.6.2.3 requires catanh(+1 + i0) to return +∞ + i0 and raise the divide-by-zero exception. Source: ISO/IEC 9899:2017, G.6.1.1 and G.6.2.3.
-2-2i-1-1i11i22iReImBranch cut — cacosh: the real axis at or below 1+1: endpoint+1: endpoint
Figure 17. The cut of cacosh: the real axis below 1, endpoint included. It is the only one of the four that is not symmetric about the origin, which is the geometric reason cacosh is neither even nor odd and its catalogue has to state the left half-plane separately. Source: ISO/IEC 9899:2017, G.6.2.1.
-2-2i-1-1i11i22iReImBranch cut — casinh, catan: above +iBranch cut — casinh, catan: below −i+i+i−i−i
Figure 18. The cuts of casinh and catan, on the imaginary axis outside −i to i. They are the images of Figure 16 under the quarter turn that relates each circular function to its hyperbolic counterpart, which is exactly what the identities in the next section say. Source: ISO/IEC 9899:2017, G.6.2.2 and G.6 paragraph 7.

Back to top

Identities and symmetry

Five of the <complex.h> functions have no special-value catalogue of their own. G.6 paragraph 7 specifies them implicitly, by an identity in terms of a function that does have one, and their special values follow from that identity rather than from a list. Two more, cabs and carg, are specified as real functions whose special cases live in Annex F.

inverse circularinverse circular · — G.6 paragraph 7casin(z) = −i·casinh(iz)inverse circular · — G.6 paragraph 7catan(z) = −i·catanh(iz)circularcircular · — G.6 paragraph 7csin(z) = −i·csinh(iz)circular · — G.6 paragraph 7ctan(z) = −i·ctanh(iz)cosine and polarcosine and polar · — G.6 paragraph 7ccos(z) = ccosh(iz)cosine and polar · — G.6 paragraph 6: both are specified by a formula in terms of a real functioncabs(x+iy) = hypot(x, y)carg(x+iy) = atan2(y, x)
Figure 19. The seven functions specified by formula rather than by enumeration. Each of the first five is a quarter turn away from a hyperbolic function, which is why Figure 18 is Figure 16 rotated. Source: ISO/IEC 9899:2017, G.6 paragraphs 6 and 7.

The symmetry reduction

Paragraph 8 states the rule that lets each catalogue below be as short as it is. For a function f satisfying f(conj(z)) = conj(f(z)), the specifications for the upper half-plane imply those for the lower half-plane. If f is also even or odd, the first quadrant implies the other three. Every catalogue in G.6.1 to G.6.4 opens with exactly that line, so a list of a dozen cases is really a list of up to four dozen.

-2-2i-1-1i11i22iReImzzconj zconj z−z, if odd−z, if odd−conj z−conj z
Figure 20. How one quadrant covers four. Conjugation reflects across the real axis; oddness or evenness sends a point to its opposite through the origin; the two together reach every quadrant from the first. Source: ISO/IEC 9899:2017, G.6 paragraph 8.
FunctionConjugate symmetryParityQuadrants a catalogue must state
cacos, cacosh, cexp, clog, csqrtyesneitherthe upper half-plane
casinh, catanh, csinh, ctanhyesoddthe first quadrant
ccoshyeseventhe first quadrant

Which part of the plane each catalogue actually enumerates. The grids in the next section are filled out to the whole plane using these rules, so a cell for a negative real part is derived rather than quoted where the annex states only the positive case. Source: ISO/IEC 9899:2017, G.6 paragraph 8 and the opening line of each of G.6.1.1 to G.6.4.2.

Back to top

The special-value catalogue

Ten of the <complex.h> functions get an explicit list of special cases in G.6. The lists are exhaustive over the argument classes that matter, but they are lists: reading one tells you what happens at a point, not what the function does to the plane. Laid out as a grid over the real-part class and the imaginary-part class, the same information shows its shape, and the shape is consistent enough to be worth naming.

Every grid below shares the same axes. Columns are the class of the real part x, from −∞ on the left to NaN on the right; rows are the class of the imaginary part y, from +∞ at the top down to NaN at the bottom, so the top three rows read like the upper half-plane. Cells marked ordinary have no clause in the annex and take the mathematical value. A cell marked not specified is one the annex genuinely leaves open, not one omitted here. Cells derived from the symmetry rules of the previous section are marked in the notes table that follows the grids.

Finite every part finite and not both zeroZero both parts are zeros, signs as writtenInfinity at least one part infinite — an infinity per G.3, even when the other part is a NaNNaN a NaN part with no infinite part

cacos

The inverse cosine. Its own entry in G.6.1; casin and catan are given by identity in G.6 paragraph 7 rather than enumerated. Specified in G.6.1.1.

cacos(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — cacos(−∞ + i+∞) returns 3π/4 − i∞3π/4 − i∞y = +∞ · x = ±0 — cacos(±0 + i+∞) returns π/2 − i∞π/2 − i∞y = +∞ · x finite ≠ 0 — cacos(x + i+∞) returns π/2 − i∞π/2 − i∞y = +∞ · x = +∞ — cacos(+∞ + i+∞) returns π/4 − i∞π/4 − i∞y = +∞ · x = NaN — cacos(NaN + i+∞) returns NaN − i∞NaN − i∞y finite > 0y finite > 0 · x = −∞ — cacos(−∞ + iy) returns π − i∞π − i∞y finite > 0 · x = ±0 — cacos(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — cacos(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — cacos(+∞ + iy) returns +0 − i∞+0 − i∞y finite > 0 · x = NaN — cacos(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — cacos(−∞ + i±0) returns π − i∞π − i∞y = ±0 · x = ±0 — cacos(±0 + i±0) returns π/2 − i0π/2 − i0y = ±0 · x finite ≠ 0 — cacos(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — cacos(+∞ + i±0) returns +0 − i∞+0 − i∞y = ±0 · x = NaN — cacos(NaN + i±0) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaNy = NaN · x = −∞ — cacos(−∞ + iNaN) returns NaN ± i∞; sign unspecifiedNaN ± i∞sign unspecifiedy = NaN · x = ±0 — cacos(±0 + iNaN) returns π/2 + iNaNπ/2 + iNaNy = NaN · x finite ≠ 0 — cacos(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — cacos(+∞ + iNaN) returns NaN ± i∞; sign unspecifiedNaN ± i∞sign unspecifiedy = NaN · x = NaN — cacos(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 21. cacos(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.1.1.

cacosh

The inverse hyperbolic cosine, and the one function here whose cut is not symmetric about the origin. Specified in G.6.2.1.

cacosh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — cacosh(−∞ + i+∞) returns +∞ + i3π/4+∞ + i3π/4y = +∞ · x = ±0 — cacosh(±0 + i+∞) returns +∞ + iπ/2+∞ + iπ/2y = +∞ · x finite ≠ 0 — cacosh(x + i+∞) returns +∞ + iπ/2+∞ + iπ/2y = +∞ · x = +∞ — cacosh(+∞ + i+∞) returns +∞ + iπ/4+∞ + iπ/4y = +∞ · x = NaN — cacosh(NaN + i+∞) returns +∞ + iNaN+∞ + iNaNy finite > 0y finite > 0 · x = −∞ — cacosh(−∞ + iy) returns +∞ + iπ+∞ + iπy finite > 0 · x = ±0 — cacosh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — cacosh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — cacosh(+∞ + iy) returns +∞ + i0+∞ + i0y finite > 0 · x = NaN — cacosh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — cacosh(−∞ + i±0) returns +∞ + iπ+∞ + iπy = ±0 · x = ±0 — cacosh(±0 + i±0) returns +0 + iπ/2+0 + iπ/2y = ±0 · x finite ≠ 0 — cacosh(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — cacosh(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — cacosh(NaN + i±0) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaNy = NaN · x = −∞ — cacosh(−∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = ±0 — cacosh(±0 + iNaN) returns NaN ± iπ/2; sign unspecifiedNaN ± iπ/2sign unspecifiedy = NaN · x finite ≠ 0 — cacosh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — cacosh(+∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = NaN — cacosh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 22. cacosh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.1.

casinh

The inverse hyperbolic sine. Odd, so the annex states only the first quadrant and the other three follow. Specified in G.6.2.2.

casinh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — casinh(−∞ + i+∞) returns −∞ + iπ/4−∞ + iπ/4y = +∞ · x = ±0 — casinh(±0 + i+∞) returns +∞ + iπ/2+∞ + iπ/2y = +∞ · x finite ≠ 0 — casinh(x + i+∞) returns +∞ + iπ/2; x > 0+∞ + iπ/2x > 0y = +∞ · x = +∞ — casinh(+∞ + i+∞) returns +∞ + iπ/4+∞ + iπ/4y = +∞ · x = NaN — casinh(NaN + i+∞) returns ±∞ + iNaN; sign unspecified±∞ + iNaNsign unspecifiedy finite > 0y finite > 0 · x = −∞ — casinh(−∞ + iy) returns −∞ + i0−∞ + i0y finite > 0 · x = ±0 — casinh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — casinh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — casinh(+∞ + iy) returns +∞ + i0+∞ + i0y finite > 0 · x = NaN — casinh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — casinh(−∞ + i±0) returns −∞ + i0−∞ + i0y = ±0 · x = ±0 — casinh(±0 + i±0) returns +0 + i0+0 + i0y = ±0 · x finite ≠ 0 — casinh(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — casinh(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — casinh(NaN + i±0) returns NaN + i0NaN + i0y = NaNy = NaN · x = −∞ — casinh(−∞ + iNaN) returns −∞ + iNaN−∞ + iNaNy = NaN · x = ±0 — casinh(±0 + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x finite ≠ 0 — casinh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — casinh(+∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = NaN — casinh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 23. casinh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.2.

catanh

The inverse hyperbolic tangent, and one of the two functions in the annex whose catalogue can raise divide-by-zero. Specified in G.6.2.3.

catanh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — catanh(−∞ + i+∞) returns −0 + iπ/2−0 + iπ/2y = +∞ · x = ±0 — catanh(±0 + i+∞) returns +0 + iπ/2+0 + iπ/2y = +∞ · x finite ≠ 0 — catanh(x + i+∞) returns +0 + iπ/2; x > 0+0 + iπ/2x > 0y = +∞ · x = +∞ — catanh(+∞ + i+∞) returns +0 + iπ/2+0 + iπ/2y = +∞ · x = NaN — catanh(NaN + i+∞) returns ±0 + iπ/2; sign unspecified±0 + iπ/2sign unspecifiedy finite > 0y finite > 0 · x = −∞ — catanh(−∞ + iy) returns −0 + iπ/2−0 + iπ/2y finite > 0 · x = ±0 — catanh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — catanh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — catanh(+∞ + iy) returns +0 + iπ/2+0 + iπ/2y finite > 0 · x = NaN — catanh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — catanh(−∞ + i±0) returns −0 + iπ/2−0 + iπ/2y = ±0 · x = ±0 — catanh(±0 + i±0) returns +0 + i0+0 + i0y = ±0 · x finite ≠ 0 — catanh(x + i±0) returns ordinary; ±1: divide-by-zeroordinary±1: divide-by-zeroy = ±0 · x = +∞ — catanh(+∞ + i±0) returns +0 + iπ/2+0 + iπ/2y = ±0 · x = NaN — catanh(NaN + i±0) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaNy = NaN · x = −∞ — catanh(−∞ + iNaN) returns −0 + iNaN−0 + iNaNy = NaN · x = ±0 — catanh(±0 + iNaN) returns +0 + iNaN+0 + iNaNy = NaN · x finite ≠ 0 — catanh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — catanh(+∞ + iNaN) returns +0 + iNaN+0 + iNaNy = NaN · x = NaN — catanh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 24. catanh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.3.

ccosh

The hyperbolic cosine. Even, and one of the three functions whose infinite results are written with cis(y) = cos(y) + i sin(y). Specified in G.6.2.4.

ccosh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — ccosh(−∞ + i+∞) returns ±∞ + iNaN; invalid±∞ + iNaNinvalidy = +∞ · x = ±0 — ccosh(±0 + i+∞) returns NaN ± i0; invalidNaN ± i0invalidy = +∞ · x finite ≠ 0 — ccosh(x + i+∞) returns NaN + iNaN; invalidNaN + iNaNinvalidy = +∞ · x = +∞ — ccosh(+∞ + i+∞) returns ±∞ + iNaN; invalid±∞ + iNaNinvalidnot specifiedy finite > 0y finite > 0 · x = −∞ — ccosh(−∞ + iy) returns +∞·cis(−y)+∞·cis(−y)y finite > 0 · x = ±0 — ccosh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — ccosh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — ccosh(+∞ + iy) returns +∞·cis(y)+∞·cis(y)y finite > 0 · x = NaN — ccosh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — ccosh(−∞ + i±0) returns +∞ − i0+∞ − i0y = ±0 · x = ±0 — ccosh(±0 + i±0) returns 1 + i01 + i0y = ±0 · x finite ≠ 0 — ccosh(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — ccosh(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — ccosh(NaN + i±0) returns NaN ± i0; sign unspecifiedNaN ± i0sign unspecifiedy = NaNy = NaN · x = −∞ — ccosh(−∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = ±0 — ccosh(±0 + iNaN) returns NaN ± i0; sign unspecifiedNaN ± i0sign unspecifiedy = NaN · x finite ≠ 0 — ccosh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — ccosh(+∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = NaN — ccosh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 25. ccosh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.4.

csinh

The hyperbolic sine. Odd, and otherwise the mirror of ccosh. Specified in G.6.2.5.

csinh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — csinh(−∞ + i+∞) returns ±∞ + iNaN; invalid±∞ + iNaNinvalidy = +∞ · x = ±0 — csinh(±0 + i+∞) returns ±0 + iNaN; invalid±0 + iNaNinvalidy = +∞ · x finite ≠ 0 — csinh(x + i+∞) returns NaN + iNaN; invalidNaN + iNaNinvalidy = +∞ · x = +∞ — csinh(+∞ + i+∞) returns ±∞ + iNaN; invalid±∞ + iNaNinvalidnot specifiedy finite > 0y finite > 0 · x = −∞ — csinh(−∞ + iy) returns −∞·cis(−y)−∞·cis(−y)y finite > 0 · x = ±0 — csinh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — csinh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — csinh(+∞ + iy) returns +∞·cis(y)+∞·cis(y)y finite > 0 · x = NaN — csinh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — csinh(−∞ + i±0) returns −∞ + i0−∞ + i0y = ±0 · x = ±0 — csinh(±0 + i±0) returns +0 + i0+0 + i0y = ±0 · x finite ≠ 0 — csinh(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — csinh(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — csinh(NaN + i±0) returns NaN + i0NaN + i0y = NaNy = NaN · x = −∞ — csinh(−∞ + iNaN) returns ±∞ + iNaN; sign unspecified±∞ + iNaNsign unspecifiedy = NaN · x = ±0 — csinh(±0 + iNaN) returns ±0 + iNaN±0 + iNaNy = NaN · x finite ≠ 0 — csinh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — csinh(+∞ + iNaN) returns ±∞ + iNaN; sign unspecified±∞ + iNaNsign unspecifiedy = NaN · x = NaN — csinh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 26. csinh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.5.

ctanh

The hyperbolic tangent. Bounded, so its infinite arguments give finite results — including the memorable 1 + i0·sin(2y). Specified in G.6.2.6.

ctanh(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — ctanh(−∞ + i+∞) returns −1 ± i0; sign unspecified−1 ± i0sign unspecifiedy = +∞ · x = ±0 — ctanh(±0 + i+∞) returns 0 + iNaN; invalid0 + iNaNinvalidy = +∞ · x finite ≠ 0 — ctanh(x + i+∞) returns NaN + iNaN; invalidNaN + iNaNinvalidy = +∞ · x = +∞ — ctanh(+∞ + i+∞) returns 1 ± i0; sign unspecified1 ± i0sign unspecifiednot specifiedy finite > 0y finite > 0 · x = −∞ — ctanh(−∞ + iy) returns −1 + i0·sin 2y−1 + i0·sin 2yy finite > 0 · x = ±0 — ctanh(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — ctanh(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — ctanh(+∞ + iy) returns 1 + i0·sin 2y1 + i0·sin 2yy finite > 0 · x = NaN — ctanh(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — ctanh(−∞ + i±0) returns −1 + i0−1 + i0y = ±0 · x = ±0 — ctanh(±0 + i±0) returns +0 + i0+0 + i0y = ±0 · x finite ≠ 0 — ctanh(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — ctanh(+∞ + i±0) returns 1 + i01 + i0y = ±0 · x = NaN — ctanh(NaN + i±0) returns NaN + i0NaN + i0y = NaNy = NaN · x = −∞ — ctanh(−∞ + iNaN) returns −1 ± i0; sign unspecified−1 ± i0sign unspecifiedy = NaN · x = ±0 — ctanh(±0 + iNaN) returns 0 + iNaN0 + iNaNy = NaN · x finite ≠ 0 — ctanh(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — ctanh(+∞ + iNaN) returns 1 ± i0; sign unspecified1 ± i0sign unspecifiedy = NaN · x = NaN — ctanh(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 27. ctanh(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.2.6.

cexp

The exponential, with the largest catalogue in the annex: thirteen enumerated cases. Specified in G.6.3.1.

cexp(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — cexp(−∞ + i+∞) returns ±0 ± i0; signs unspecified±0 ± i0signs unspecifiedy = +∞ · x = ±0 — cexp(±0 + i+∞) returns NaN + iNaN; invalidNaN + iNaNinvalidy = +∞ · x finite ≠ 0 — cexp(x + i+∞) returns NaN + iNaN; invalidNaN + iNaNinvalidy = +∞ · x = +∞ — cexp(+∞ + i+∞) returns ±∞ + iNaN; invalid±∞ + iNaNinvalidnot specifiedy finite > 0y finite > 0 · x = −∞ — cexp(−∞ + iy) returns +0·cis(y)+0·cis(y)y finite > 0 · x = ±0 — cexp(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — cexp(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — cexp(+∞ + iy) returns +∞·cis(y)+∞·cis(y)y finite > 0 · x = NaN — cexp(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — cexp(−∞ + i±0) returns +0 + i0+0 + i0y = ±0 · x = ±0 — cexp(±0 + i±0) returns 1 + i01 + i0y = ±0 · x finite ≠ 0 — cexp(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — cexp(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — cexp(NaN + i±0) returns NaN + i0NaN + i0y = NaNy = NaN · x = −∞ — cexp(−∞ + iNaN) returns ±0 ± i0; signs unspecified±0 ± i0signs unspecifiedy = NaN · x = ±0 — cexp(±0 + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x finite ≠ 0 — cexp(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — cexp(+∞ + iNaN) returns ±∞ + iNaN; sign unspecified±∞ + iNaNsign unspecifiedy = NaN · x = NaN — cexp(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 28. cexp(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.3.1.

clog

The natural logarithm, and the other of the two: it raises divide-by-zero at the origin, from either side of the cut. Specified in G.6.3.2.

clog(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — clog(−∞ + i+∞) returns +∞ + i3π/4+∞ + i3π/4y = +∞ · x = ±0 — clog(±0 + i+∞) returns +∞ + iπ/2+∞ + iπ/2y = +∞ · x finite ≠ 0 — clog(x + i+∞) returns +∞ + iπ/2+∞ + iπ/2y = +∞ · x = +∞ — clog(+∞ + i+∞) returns +∞ + iπ/4+∞ + iπ/4y = +∞ · x = NaN — clog(NaN + i+∞) returns +∞ + iNaN+∞ + iNaNy finite > 0y finite > 0 · x = −∞ — clog(−∞ + iy) returns +∞ + iπ+∞ + iπy finite > 0 · x = ±0 — clog(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — clog(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — clog(+∞ + iy) returns +∞ + i0+∞ + i0y finite > 0 · x = NaN — clog(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — clog(−∞ + i±0) returns +∞ + iπ+∞ + iπy = ±0 · x = ±0 — clog(±0 + i±0) returns −∞ + i0 or iπ; divide-by-zero−∞ + i0 or iπdivide-by-zeroy = ±0 · x finite ≠ 0 — clog(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — clog(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — clog(NaN + i±0) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaNy = NaN · x = −∞ — clog(−∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = ±0 — clog(±0 + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x finite ≠ 0 — clog(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — clog(+∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = NaN — clog(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 29. clog(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.3.2.

csqrt

The square root. Its top row is the only one in the annex that is constant across every real-part class, NaN included. Specified in G.6.4.2.

csqrt(z)x = −∞x = ±0x finite ≠ 0x = +∞x = NaNy = +∞y = +∞ · x = −∞ — csqrt(−∞ + i+∞) returns +∞ + i∞+∞ + i∞y = +∞ · x = ±0 — csqrt(±0 + i+∞) returns +∞ + i∞+∞ + i∞y = +∞ · x finite ≠ 0 — csqrt(x + i+∞) returns +∞ + i∞+∞ + i∞y = +∞ · x = +∞ — csqrt(+∞ + i+∞) returns +∞ + i∞+∞ + i∞y = +∞ · x = NaN — csqrt(NaN + i+∞) returns +∞ + i∞+∞ + i∞y finite > 0y finite > 0 · x = −∞ — csqrt(−∞ + iy) returns +0 + i∞+0 + i∞y finite > 0 · x = ±0 — csqrt(±0 + iy) returns ordinaryordinaryno clausey finite > 0 · x finite ≠ 0 — csqrt(x + iy) returns ordinaryordinaryno clausey finite > 0 · x = +∞ — csqrt(+∞ + iy) returns +∞ + i0+∞ + i0y finite > 0 · x = NaN — csqrt(NaN + iy) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = ±0y = ±0 · x = −∞ — csqrt(−∞ + i±0) returns +0 + i∞+0 + i∞y = ±0 · x = ±0 — csqrt(±0 + i±0) returns +0 + i0+0 + i0y = ±0 · x finite ≠ 0 — csqrt(x + i±0) returns ordinaryordinaryno clausey = ±0 · x = +∞ — csqrt(+∞ + i±0) returns +∞ + i0+∞ + i0y = ±0 · x = NaN — csqrt(NaN + i±0) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaNy = NaN · x = −∞ — csqrt(−∞ + iNaN) returns NaN ± i∞; sign unspecifiedNaN ± i∞sign unspecifiedy = NaN · x = ±0 — csqrt(±0 + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x finite ≠ 0 — csqrt(x + iNaN) returns NaN + iNaN; opt. invalidNaN + iNaNopt. invalidy = NaN · x = +∞ — csqrt(+∞ + iNaN) returns +∞ + iNaN+∞ + iNaNy = NaN · x = NaN — csqrt(NaN + iNaN) returns NaN + iNaNNaN + iNaN
Figure 30. csqrt(x + iy) over every argument class. Source: ISO/IEC 9899:2017, G.6.4.2.

Exceptions and unspecified signs

Two kinds of caveat appear on the grids above and neither is visible in a return value: whether the call raises a floating-point exception, and whether a sign in the result is unspecified. Both matter to a caller — the first because a later fetestexcept will see it, the second because a test on the sign of a part is not portable. This table is generated from the same cells as the grids.

Every row is a case where the return value alone does not tell you what happened.

CallResultCaveatClause
cacos(NaN + iy)NaN + iNaNopt. invalidG.6.1.1
cacos(NaN + i±0)NaN + iNaNopt. invalidG.6.1.1
cacos(−∞ + iNaN)NaN ± i∞sign unspecifiedG.6.1.1
cacos(x + iNaN)NaN + iNaNopt. invalidG.6.1.1
cacos(+∞ + iNaN)NaN ± i∞sign unspecifiedG.6.1.1
cacosh(NaN + iy)NaN + iNaNopt. invalidG.6.2.1
cacosh(NaN + i±0)NaN + iNaNopt. invalidG.6.2.1
cacosh(±0 + iNaN)NaN ± iπ/2sign unspecifiedG.6.2.1
cacosh(x + iNaN)NaN + iNaNopt. invalidG.6.2.1
casinh(x + i+∞)+∞ + iπ/2x > 0G.6.2.2
casinh(NaN + i+∞)±∞ + iNaNsign unspecifiedG.6.2.2
casinh(NaN + iy)NaN + iNaNopt. invalidG.6.2.2
casinh(±0 + iNaN)NaN + iNaNopt. invalidG.6.2.2
casinh(x + iNaN)NaN + iNaNopt. invalidG.6.2.2
catanh(x + i+∞)+0 + iπ/2x > 0G.6.2.3
catanh(NaN + i+∞)±0 + iπ/2sign unspecifiedG.6.2.3
catanh(NaN + iy)NaN + iNaNopt. invalidG.6.2.3
catanh(x + i±0)ordinary±1: divide-by-zeroG.6.2.3
catanh(NaN + i±0)NaN + iNaNopt. invalidG.6.2.3
catanh(x + iNaN)NaN + iNaNopt. invalidG.6.2.3
ccosh(−∞ + i+∞)±∞ + iNaNinvalidG.6.2.4
ccosh(±0 + i+∞)NaN ± i0invalidG.6.2.4
ccosh(x + i+∞)NaN + iNaNinvalidG.6.2.4
ccosh(+∞ + i+∞)±∞ + iNaNinvalidG.6.2.4
ccosh(NaN + iy)NaN + iNaNopt. invalidG.6.2.4
ccosh(NaN + i±0)NaN ± i0sign unspecifiedG.6.2.4
ccosh(±0 + iNaN)NaN ± i0sign unspecifiedG.6.2.4
ccosh(x + iNaN)NaN + iNaNopt. invalidG.6.2.4
csinh(−∞ + i+∞)±∞ + iNaNinvalidG.6.2.5
csinh(±0 + i+∞)±0 + iNaNinvalidG.6.2.5
csinh(x + i+∞)NaN + iNaNinvalidG.6.2.5
csinh(+∞ + i+∞)±∞ + iNaNinvalidG.6.2.5
csinh(NaN + iy)NaN + iNaNopt. invalidG.6.2.5
csinh(−∞ + iNaN)±∞ + iNaNsign unspecifiedG.6.2.5
csinh(x + iNaN)NaN + iNaNopt. invalidG.6.2.5
csinh(+∞ + iNaN)±∞ + iNaNsign unspecifiedG.6.2.5
ctanh(−∞ + i+∞)−1 ± i0sign unspecifiedG.6.2.6
ctanh(±0 + i+∞)0 + iNaNinvalidG.6.2.6
ctanh(x + i+∞)NaN + iNaNinvalidG.6.2.6
ctanh(+∞ + i+∞)1 ± i0sign unspecifiedG.6.2.6
ctanh(NaN + iy)NaN + iNaNopt. invalidG.6.2.6
ctanh(−∞ + iNaN)−1 ± i0sign unspecifiedG.6.2.6
ctanh(x + iNaN)NaN + iNaNopt. invalidG.6.2.6
ctanh(+∞ + iNaN)1 ± i0sign unspecifiedG.6.2.6
cexp(−∞ + i+∞)±0 ± i0signs unspecifiedG.6.3.1
cexp(±0 + i+∞)NaN + iNaNinvalidG.6.3.1
cexp(x + i+∞)NaN + iNaNinvalidG.6.3.1
cexp(+∞ + i+∞)±∞ + iNaNinvalidG.6.3.1
cexp(NaN + iy)NaN + iNaNopt. invalidG.6.3.1
cexp(−∞ + iNaN)±0 ± i0signs unspecifiedG.6.3.1
cexp(±0 + iNaN)NaN + iNaNopt. invalidG.6.3.1
cexp(x + iNaN)NaN + iNaNopt. invalidG.6.3.1
cexp(+∞ + iNaN)±∞ + iNaNsign unspecifiedG.6.3.1
clog(NaN + iy)NaN + iNaNopt. invalidG.6.3.2
clog(±0 + i±0)−∞ + i0 or iπdivide-by-zeroG.6.3.2
clog(NaN + i±0)NaN + iNaNopt. invalidG.6.3.2
clog(±0 + iNaN)NaN + iNaNopt. invalidG.6.3.2
clog(x + iNaN)NaN + iNaNopt. invalidG.6.3.2
csqrt(NaN + iy)NaN + iNaNopt. invalidG.6.4.2
csqrt(NaN + i±0)NaN + iNaNopt. invalidG.6.4.2
csqrt(−∞ + iNaN)NaN ± i∞sign unspecifiedG.6.4.2
csqrt(±0 + iNaN)NaN + iNaNopt. invalidG.6.4.2
csqrt(x + iNaN)NaN + iNaNopt. invalidG.6.4.2

All 63 flagged cells across the ten catalogues. “Opt. invalid” is the annex’s optionally raises the invalid floating-point exception: an implementation may raise it and may not, so a caller must neither rely on it nor be surprised by it. Source: ISO/IEC 9899:2017, G.6.1.1 to G.6.4.2.

Back to top

What the catalogue adds up to

Summed over the ten grids, the annex is less a list of oddities than a statement about where the complex plane stops being ordinary. Of the 196 argument classes the ten grids cover, 30 take the ordinary mathematical value and every one of the remaining 166 is pinned to a specific result — and 4 combinations are left open by the annex altogether.

cacoscacos · Finite: 44cacos · Infinity: 1111cacos · NaN: 5520cacoshcacosh · Finite: 44cacosh · Infinity: 1111cacosh · NaN: 5520casinhcasinh · Finite: 33casinh · Zero: 11casinh · Infinity: 1111casinh · NaN: 5520catanhcatanh · Finite: 1212catanh · Zero: 11catanh · NaN: 7720ccoshccosh · Finite: 44ccosh · Infinity: 88ccosh · NaN: 7719csinhcsinh · Finite: 33csinh · Zero: 11csinh · Infinity: 88csinh · NaN: 7719ctanhctanh · Finite: 1111ctanh · Zero: 11ctanh · NaN: 7719cexpcexp · Finite: 44cexp · Zero: 44cexp · Infinity: 44cexp · NaN: 7719clogclog · Finite: 33clog · Infinity: 1212clog · NaN: 5520csqrtcsqrt · Finite: 33csqrt · Zero: 11csqrt · Infinity: 1111csqrt · NaN: 5520FiniteZeroInfinityNaN
Figure 31. The argument classes of each grid, by the kind of value they return; each bar carries its own total, which is nineteen rather than twenty for the four functions with a combination the annex leaves unspecified. Every function is dominated by one class: the inverse and logarithmic functions by infinities, because their cuts run to infinity, and the bounded functions by NaNs, because an infinite argument to a periodic function has no limit to converge to. Source: derived from ISO/IEC 9899:2017, G.6.1.1 to G.6.4.2.
cacos — 3. 3 of 20 cells raise or may raise a floating-point exceptioncacos3cacosh — 3. 3 of 20 cells raise or may raise a floating-point exceptioncacosh3casinh — 3. 3 of 20 cells raise or may raise a floating-point exceptioncasinh3catanh — 4. 4 of 20 cells raise or may raise a floating-point exceptioncatanh4ccosh — 6. 6 of 19 cells raise or may raise a floating-point exceptionccosh6csinh — 6. 6 of 19 cells raise or may raise a floating-point exceptioncsinh6ctanh — 4. 4 of 19 cells raise or may raise a floating-point exceptionctanh4cexp — 6. 6 of 19 cells raise or may raise a floating-point exceptioncexp6clog — 5. 5 of 20 cells raise or may raise a floating-point exceptionclog5csqrt — 4. 4 of 20 cells raise or may raise a floating-point exceptioncsqrt406
Figure 32. Cells that raise, or may raise, a floating-point exception. Most of them are the optional “invalid” on a NaN argument, which every catalogue carries; the ones that raise it unconditionally are the periodic functions given an infinite imaginary part, where there is no limit to converge to. Only catanh and clog can raise divide-by-zero. Source: derived from ISO/IEC 9899:2017, G.6.1.1 to G.6.4.2.
FunctionClauseCases enumeratedCells returning an infinityCells returning a NaN
cacosG.6.1.112115
cacoshG.6.2.112115
casinhG.6.2.210115
catanhG.6.2.31107
ccoshG.6.2.41287
csinhG.6.2.51287
ctanhG.6.2.61107
cexpG.6.3.11347
clogG.6.3.212125
csqrtG.6.4.29115

Enumerated cases are the bullets of each clause, excluding the symmetry identity that opens it; the two right-hand columns are counted from the grids above. The totals differ because one clause can cover several argument classes — csqrt(x + i∞) alone covers a whole row. Sources: ISO/IEC 9899:2017, G.6.1.1 to G.6.4.2, and the grids in the previous section.

Back to top

Type-generic macros and imaginary arguments

G.7 extends <tgmath.h> so that a type-generic macro accepts an imaginary argument, and it does not simply widen it to complex. The macro expands to an expression whose type is real, imaginary or complex depending on the function, which means a chain of type-generic calls on an imaginary value can stay one component wide from end to end.

Result type for an imaginary argumentMacros
realcos, cosh, fabs, carg, cimag, creal
imaginarysin, tan, sinh, tanh, asin, atan, asinh, atanh
complexall the others

The three outcomes. Six macros collapse an imaginary argument to a real result, eight keep it imaginary, and everything else widens. Source: ISO/IEC 9899:2017, G.7 paragraph 1.

real float, double, long doubleimaginary _Imaginary, Annex G onlycomplex _Complex
cosinecosine · — G.7 paragraph 2cos(iy) = cosh(y)realcosine · — G.7 paragraph 2cosh(iy) = cos(y)realsinesine · — G.7 paragraph 2sin(iy) = i·sinh(y)imaginarysine · — G.7 paragraph 2sinh(iy) = i·sin(y)imaginarytangenttangent · — G.7 paragraph 2tan(iy) = i·tanh(y)imaginarytangent · — G.7 paragraph 2tanh(iy) = i·tan(y)imaginaryinverse sineinverse sine · — G.7 paragraph 2asin(iy) = i·asinh(y)imaginaryinverse sine · — G.7 paragraph 2asinh(iy) = i·asin(y)imaginaryinverse tangentinverse tangent · — G.7 paragraph 2atan(iy) = i·atanh(y)imaginaryinverse tangent · — G.7 paragraph 2atanh(iy) = i·atan(y)imaginary
Figure 33. The ten identities of G.7 paragraph 2, each written in terms of a real function. The left column is the circular function and the right column its hyperbolic counterpart; reading a row across is the quarter turn, and it is the same quarter turn that maps Figure 16 onto Figure 18. Source: ISO/IEC 9899:2017, G.7 paragraph 2.

Every right-hand side is a call to a real function, which is what makes the extension cheap: an implementation of tan for an imaginary argument is tanh on the coefficient, with the result reinterpreted as imaginary. No complex arithmetic is generated at all.

Back to top

How this reached the standard

C99complex types, Annex GC11__STDC_NO_COMPLEX__C11CMPLX macros addedC17Annex G unchangedC23still conditional
Figure 34. The standardisation path of complex arithmetic in C. The direction of travel is towards making things optional: the types arrived mandatory, became optional in C11, and the imaginary types were never mandatory at all. Sources: ISO/IEC 9899:1999, :2011, :2018 and :2024 front matter and Annex G.

The practical position today is that the complex types are widely available and Annex G is not. An implementation may define __STDC_IEC_559_COMPLEX__ only if it meets everything on this page, and most do not: imaginary types in particular are rarely provided, which is why CMPLX exists and why the third row of Figure 14 is a portability hazard rather than a curiosity. Code that needs the infinity behaviour without the annex has to reimplement the recovery paths of G.5.1 itself — which is exactly what the two worked examples are for.

Back to top

Sources

  • ISO/IEC 9899:2017 (C17), N2176 ballot text — Annex G, IEC 60559-compatible complex arithmetic: G.2 (types), G.3 (conventions), G.5.1 (multiplicative operators, including Examples 1 and 2), G.5.2 (additive operators), G.6 (<complex.h>) and G.7 (<tgmath.h>). Every formula table, infinity property and special-value clause on this page is from that annex, and the two code examples are reproduced from it with only whitespace changed.
  • ISO/IEC 9899:2017 — 6.2.5 (types), 6.10.8.3 (conditional feature macros), 7.3 (<complex.h>, including the 7.3 definition of I that G.6 replaces), 7.3.4 (CX_LIMITED_RANGE) and 7.3.9.3 (the CMPLX macros).
  • ISO/IEC 60559:2020 / IEEE 754-2019 — the arithmetic the annex is compatible with, and the source of the binary64 exponent limits used in Figure 12.
  • ISO/IEC 9899:2011 and :2024 — for the changes in status recorded in Figure 34.

Figures 6, 7, 12, 31 and 32 and the two right-hand columns of the census table are derived counts, computed by this page’s builder from the tables above rather than quoted; the caption on each says so. Where a grid cell covers a case the annex states only for the upper half-plane or the first quadrant, it is filled in using the symmetry rules of G.6 paragraph 8, as described in the symmetry section.

Back to top