SC22/WG14 N820 Splitting Annex G Clive D.W. Feather clive@demon.net 1998-04-15 Abstract ======== In the UK response to CD1 the UK suggested splitting Annex G into two parts. Annex G currently gives a specification for IEC 559 compatible complex types *and* for imaginary types, all conflated. These are separate concepts which can each be useful, and should be separated. This paper proposes new wording to this end. Conceptual changes ================== Annex G is split into two annexes, GI and GC. The former consists of the old G.2, G.3, and G.6, while the latter consists of G.5 (except one paragraph). G.1 and G.4 are divided between the two. Both annexes are normative. Wherever text appears here without a change bar, it is taken directly from CD1. Other references to Annex G are as follows: FN28 and FN186 should refer to GI. 6.8.8 and 7.8.2.1 should refer to GC. K.1 should refer to GC.3. New wording =========== | Annex GI | (normative) | Imaginary Types GI.1 Introduction | [#1] This annex specifies imaginary types. An implementation shall | either conform to all the requirements of this annex, and shall define | the macro _Imaginary_I in , or it shall not provide such | types and shall not define _Imaginary_I. GI.2 Types [#1] There are three imaginary types, designated as float | _Imaginary, double _Imaginary, and long double _Imaginary. The imaginary types (along with the real floating and complex types) are floating types. [#2] For imaginary types, the corresponding real type is given by | deleting the keyword _Imaginary from the type name. [#3] Each imaginary type has the same representation and alignment requirements as the corresponding real type. The value of an object of imaginary type is the value of the real representation times the imaginary unit. [#4] The imaginary type-domain comprises the imaginary types. GI.3 Conversions GI.3.1 Imaginary types [#1] Conversions among imaginary types follow rules analogous to those for real floating types. GI.3.2 Real and imaginary [#1] When a value of imaginary type is converted to a real type, the result is a positive zero. [#2] When a value of real type is converted to an imaginary type, the result is a positive imaginary zero. GI.3.3 Imaginary and complex [#1] When a value of imaginary type is converted to a complex type, the real part of the complex result value is a positive zero and the imaginary part of the complex result value is determined by the conversion rules for the corresponding real types. [#2] When a value of complex type is converted to an imaginary type, the real part of the complex value is discarded and the value of the imaginary part is converted according to the conversion rules for the corresponding real types. GI.4 Binary operators [#1] The following subclauses supplement 6.3 in order to specify the type of the result for an operation with an imaginary operand. | [#2] For the multiplicative operators, if one operand has real type and the other operand has imaginary type, then the result has imaginary type. If both operands have imaginary type, then the result has real type. (If either operand has complex type, then the result has complex type.) | [#3] For the additive operators, if one operand has real type and the other operand has imaginary type, then the result has complex type. If both operands have imaginary type, then the result has imaginary type. (If either operand has complex type, then the result has complex type.) GI.5 [#1] The macro _Imaginary_I is defined, and the macro I is defined to be _Imaginary_I (7.8). GI.6 [#1] Type-generic macros that accept complex arguments also accept imaginary arguments. If an argument is imaginary, the macro expands to an expression whose type is real, imaginary, or complex, as appropriate for the particular function: if the argument is imaginary, then the types of cos, cosh, fabs, carg, cimag, and creal are real; the types of sin, tan, sinh, tanh, asin, atan, asinh, and atanh are imaginary; and the types of the others are complex. [#2] Given an imaginary argument, each of the type-generic macros cos, sin, tan, cosh, sinh, tanh, asin, atan, asinh, atanh is specified by a formula in terms of real functions: cos(i*y) = cosh(y) sin(i*y) = i*sinh(y) tan(i*y) = i*tanh(y) cosh(i*y) = cos(y) sinh(i*y) = i*sin(y) tanh(i*y) = i*tan(y) asin(i*y) = i*asinh(y) atan(i*y) = i*atanh(y) asinh(i*y) = i*asin(y) atanh(i*y) = i*atan(y) | Annex GC | (normative) | IEC 559-compatible complex arithmetic GC.1 Introduction | [#1] This annex supplements Annex F to specify complex arithmetic | for compatibility with IEC 559 real floating-point arithmetic. | An implementation that defines __STDC_IEC_559_COMPLEX__ conforms to the | specification in this annex. Where a binding between the C language and | IEC 559 is indicated, the IEC 559-specified behavior is adopted by | reference, unless stated otherwise. GC.2 Binary operators [#1] For most operand types, the value of the result of a binary operator with an imaginary or complex operand is completely determined, with reference to real arithmetic, by the usual mathematical formula. For some operand types, the usual mathematical formula is problematic because of its treatment of infinities and because of undue overflow or underflow; in these cases the result satisfies certain properties (specified in this subclause), but is not completely determined. GC.2.1 Multiplicative operators Semantics [#1] If the operands are not both complex, then the result and exception behavior of the * operator is defined by the usual mathematical formula: [[table omitted]] [#2] If the second operand is not complex, then the result and exception behavior of the / operator is defined by the usual mathematical formula: [[table omitted]] [#3] A complex or imaginary value with at least one infinite part is regarded as an infinity (even if its other part is a NaN). A complex or imaginary value is a finite number if each of its parts is a finite number (neither infinite nor NaN). A complex or imaginary value is a zero if each of its parts is a zero. The * and / operators satisfy the following infinity properties for all real, imaginary, and complex operands:289 - if one operand is an infinity and the other operand is a nonzero finite number or an infinity, then the result of the * operator is an infinity; - if the first operand is an infinity and the second operand is a finite number, then the result of the / operator is an infinity; - if the first operand is a finite number and the second operand is an infinity, then the result of the / operator is a zero; - if the first operand is a nonzero finite number or an infinity and the second operand is a zero, then the result of the / operator is an infinity. [#4] If both operands of the * operator are complex or if the second operand of the / operator is complex, the operator raises exceptions if appropriate for the calculation of the parts of the result, and may raise spurious exceptions. Examples [#5] 1. Multiplication of double complex operands could be implemented as follows. Note that the imaginary unit I has imaginary type (see GC.3). #include #include /* 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; /* Recover infinities that computed as NaN+iNaN ... */ if (isnan(x) && isnan(y)) { int recalc = 0; if ( isinf(a) || isinf(b) ) { /* z is infinite */ /* "Box" the infinity ... */ a = copysign(isinf(a) ? 1.0 : 0.0, a); b = copysign(isinf(b) ? 1.0 : 0.0, b); /* Change NaNs in the other factor to 0 ... */ 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 */ /* "Box" the infinity ... */ c = copysign(isinf(c) ? 1.0 : 0.0, c); d = copysign(isinf(d) ? 1.0 : 0.0, d); /* Change NaNs in the other factor to 0 ... */ if (isnan(a)) a = copysign(0.0, a); if (isnan(b)) b = copysign(0.0, b); recalc = 1; } if (!recalc) { /* *Recover infinities from overflow cases ... */ if (isinf(ac) || isinf(bd) || isinf(ad) || isinf(bc)) { /* Change all 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; } In ordinary (finite) cases, the cost to satisfy the infinity property for the * operator is only one isnan test. This implementation opts for performance over guarding against undue overflow and underflow. 2. Division of two double complex operands could be implemented as follows. #include #include /* 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 non-zero/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 (isinf(logbw) && 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; } [#6] Scaling the denominator alleviates the main overflow and underflow problem, which is more serious than for multiplication. In the spirit of the multiplication example above, this code does not defend against overflow and underflow in the calculation of the numerator. Scaling with the scalbn function, instead of with division, provides better roundoff characteristics. GC.2.2 Additive operators Semantics [#1] In all cases the result and exception behavior of a + or - operator is defined by the usual mathematical formula: [[table omitted]] GC.3 [#1] This subclause contains specification for the functions that is particularly suited to IEC 559 implementations. [#2] The functions are continuous onto both sides of their branch cuts, taking into account the sign of zero. For example, csqrt( -2 _ 0*I) == _sqrt(2)*I. [#3] Since complex and imaginary values are composed of real values, each function may be regarded as computing real values from real values. Except as noted, the functions treat real infinities, NaNs, signed zeros, subnormals, and the exception flags in a manner consistent with the specification for real functions in F.9. [#4] The functions conj, cimag, cproj, and creal are fully specified for all implementations, including IEC 559 ones, in 7.9.2. These functions raise no exceptions. [#5] Each of the functions cabs and carg is specified by a formula in terms of a real function (whose special cases are covered in annex F): cabs(x + i*y) = hypot(x, y) carg(x + i*y) = atan2(y, x) [#6] Each of the functions casin, catan, ccos, csin, ctan, and cpow is specified implicitly by a formula in terms of other complex functions (whose special cases are specified below): casin(z) = -i*casinh(i*z) catan(z) = -i*catanh(i*z) ccos(z) = ccosh(i*z) csin(z) = -i*csinh(i*z) ctan(z) = -i*ctanh(i*z) cpow(z, c) = cexp(c * clog(z)) [#7] For the other functions, the following subclauses specify behavior for special cases, including treatment of the invalid and divide-by-zero exceptions. For a function f satisfying f(conj(z)) = conj(f(z)), the specification for the upper half- plane implies the specification for the lower half-plane; if also the function f is either even, f(-z) = f(z), or odd, f(-z) = -f(z), then the specification for the first quadrant implies the specification for the other three quadrants. GC.3.1 The cacos function [#1] - cacos(conj(z)) = conj(cacos(z)). - cacos(_0+i0) returns pi/2-i0. - cacos(-oo+ioo) returns 3pi/4-ioo. - cacos(+oo+ioo) returns pi/4-ioo. - cacos(x+ioo) returns pi/2-ioo, for finite x. - cacos(-oo+iy) returns pi-ioo, for positive-signed finite y. - cacos(+oo+iy) returns +0-ioo, for positive-signed finite y. - cacos(_oo+iNAN) returns NaN_i oo (where the sign of the imaginary part of the result is unspecified). - cacos(_0+iNAN) returns pi/2+iNaN. - cacos(NAN+ioo) returns NaN-ioo. - cacos(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for nonzero finite x. - cacos(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for finite y. - cacos(NAN+iNAN) returns NaN+iNaN. GC.3.2 The cacosh function [#1] - cacosh(conj(z)) = conj(cacosh(z)). - cacosh(_0+i0) returns +0+ipi/2. - cacosh(-oo+ioo) returns +oo+i3pi/4. - cacosh(+oo+ioo) returns +oo+ipi/4. - cacosh(x+ioo) returns +oo+ipi/2, for finite x. - cacosh(-oo+iy) returns +oo+ipi, for positive-signed finite y. - cacosh(+oo+iy) returns +oo+i0, for positive-signed finite y. - cacosh(NAN+ioo) returns +oo+iNaN. - cacosh(_oo+iNAN) returns +oo+iNaN. - cacosh(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite x. - cacosh(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for finite y. - cacosh(NAN+iNAN) returns NaN+iNaN. GC.3.3 The casinh function - casinh(conj(z)) = conj(casinh(z)) and casinh is odd. - casinh(+0+i0) returns 0+i0. - casinh(oo+ioo) returns +oo+ipi/4. - casinh(x+ioo) returns +oo+ipi/2 for positive-signed finite x. - casinh(+oo+iy) returns +oo+i0 for positive-signed finite y. - casinh(NAN+ioo) returns _oo+iNaN (where the sign of the real part of the result is unspecified). - casinh(+oo+iNAN) returns +oo+iNaN. - casinh(NAN+i0) returns NaN+i0. - casinh(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for finite nonzero y. - casinh(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite x. - casinh(NAN+iNAN) returns NaN+iNaN. GC.3.4 The catanh function [#1] - catanh(conj(z)) = conj(catanh(z))and catanh is odd. - catanh(+0+i0) returns +0+i0. - catanh(+oo+ioo) returns +0+ipi/2. - catanh(+oo+iy) returns +0+ipi/2, for finite positive-signed y. - catanh(x+ioo) returns +0+ipi/2, for finite positive-signed x. - catanh(+0+iNAN) returns +0+iNaN. - catanh(NAN+ioo) returns _0+ipi/2 (where the sign of the real part of the result is unspecified). - catanh(+oo+iNAN) returns +0+iNaN. - catanh(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for finite y. - catanh(x+iNAN) returns NAN+iNAN and optionally raises the invalid exception for nonzero finite x. - catanh(NAN+iNAN) returns NaN+iNaN. GC.3.5 The ccosh function [#1] - ccosh(conj(z)) = conj(ccosh(z)) and ccosh is even. - ccosh(+0+i0) returns 1+i0. - ccosh(+0+ioo) returns NaN _ i0 (where the sign of the imaginary part of the result is unspecified) and raises the invalid exception. - ccosh(+oo+i0) returns +oo+i0. - ccosh(+oo+ioo) returns +oo + iNaN and raises the invalid exception. - ccosh(x+ioo) returns NaN + iNaN and raises the invalid exception, for finite nonzero x. - ccosh(+oo+iy) returns (+oo)*cis(y), for finite nonzero y.290 - ccosh(+0+iNaN) returns NaN _ i0 (where the sign of the imaginary part of the result is unspecified). - ccosh(+oo+iNaN) returns +oo+iNaN. - ccosh(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite nonzero x. - ccosh(NAN+i0) returns NaN _ i0 (where the sign of the imaginary part of the result is unspecified). - ccosh(NAN+iy) returns NAN+iNAN and optionally raises the invalid exception, for all nonzero numbers y. - ccosh(NAN+iNAN) returns NaN+iNaN. GC.3.6 The csinh function [#1] - csinh(conj(z)) = conj(csinh(z)) and csinh is odd. - csinh(+0+i0) returns +0+i0. - csinh(+0+ioo) returns _0+iNaN (where the sign of the real part of the result is unspecified) and raises the invalid exception. - csinh(+oo+i0) returns +oo+i0. - csinh(+oo+ioo) returns _oo+iNaN (where the sign of the real part of the result is unspecified) and raises the invalid exception. - csinh(+oo+iy) returns (+oo)*cis(y), for positive finite y. - csinh(x+ioo) returns NaN + iNaN and raises the invalid exception, for positive finite x. - csinh(+0+iNAN) returns _0+iNaN (where the sign of the real part of the result is unspecified). - csinh(+oo+iNAN) returns _oo+iNaN (where the sign of the real part of the result is unspecified). - csinh(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite nonzero x. - csinh(NAN+i0) returns NaN+i0. - csinh(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for all nonzero numbers y. - csinh(NAN+iNAN) returns NaN+iNaN. GC.3.7 The ctanh function [#1] - ctanh(conj(z)) = conj(ctanh(z))and ctanh is odd. - ctanh(+0+i0) returns +0+i0. - ctanh(+oo+iy) returns 1+i0, for all positive-signed numbers y. - ctanh(x+ioo) returns NaN + iNaN and raises the invalid exception, for finite x. - ctanh(+oo+iNAN) returns 1 _ i0 (where the sign of the imaginary part of the result is unspecified). - ctanh(NAN+i0) returns NaN+i0. - ctanh(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for all nonzero numbers y. - ctanh(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite x. - ctanh(NAN+iNAN) returns NaN+iNaN. GC.3.8 The cexp function [#1] - cexp(conj(z)) = conj(cexp(z)). - cexp(_0+i0) returns 1+i0. - cexp(+oo+i0) returns +oo+i0. - cexp(-oo+ioo) returns _0_i0 (where the signs of the real and imaginary parts of the result are unspecified). - cexp(+oo+ioo) returns _ oo + iNaN and raises the invalid exception (where the sign of the real part of the result is unspecified). - cexp(x+ioo ) returns NaN + iNaN and raises the invalid exception, for finite x. - cexp(-oo+iy) returns +0*cis(y), for finite y. - cexp(+oo+iy) returns +oo*cis(y), for finite nonzero y. - cexp(-oo+iNAN) returns _0_i0 (where the signs of the real and imaginary parts of the result are unspecified). - cexp(+oo+iNAN) returns _oo+iNaN (where the sign of the real part of the result is unspecified). - cexp(NAN+i0) returns NaN+i0. - cexp(NAN+iy) returns NaN+ iNaN and optionally raises the invalid exception, for all non-zero numbers y. - cexp(x+iNAN) returns NaN+ iNaN and optionally raises the invalid exception, for finite x. - cexp(NAN+iNAN) returns NaN+iNaN. GC.3.9 The clog function [#1] - clog(conj(z)) = conj(clog(z)). - clog(-0+i0) returns -oo+ipi and raises the divide-by-zero exception. - clog(+0+i0) returns -oo+i0 and raises the divide-by-zero exception. - clog(-oo+ioo) returns +oo+i3pi/4. - clog(+oo+ioo) returns +oo+ipi/4. - clog(x+ioo) returns +oo+ipi/2, for finite x. - clog(-oo+iy) returns +oo+ipi, for finite positive-signed y. - clog(+oo+iy) returns +oo+i0, for finite positive-signed y. - clog(_oo+iNAN) returns +oo+iNaN. - clog(NAN+ioo) returns +oo+iNaN. - clog(x+iNAN) returns NaN+ iNaN and optionally raises the invalid exception, for finite x. - clog(NAN+iy) returns NaN+ iNaN and optionally raises the invalid exception, for finite y. - clog(NAN+iNAN) returns NaN+iNaN. GC.3.10 The csqrt function [#1] - csqrt(conj(z)) = conj(csqrt(z)). - csqrt(_0+i0) returns +0+i0. - csqrt(-oo+iy) returns +0+ioo, for finite positive-signed y. - csqrt(+oo+iy) returns +oo+i0, for finite positive-signed y. - csqrt(x+ioo) returns +oo+ioo, for all x (including NaN). - csqrt(-oo+iNAN) returns NaN_i oo (where the sign of the imaginary part of the result is unspecified). - csqrt(+oo+iNAN) returns +oo+iNaN. - csqrt(x+iNAN) returns NaN+iNaN and optionally raises the invalid exception, for finite x. - csqrt(NAN+iy) returns NaN+iNaN and optionally raises the invalid exception, for finite y. - csqrt(NAN+iNAN) returns NaN+iNaN. __________ 289. These properties are already implied for those cases covered in the tables, but are required for all cases (at least where the state for CX_LIMITED_RANGE is off). 290. cis(y) is defined by cos(y)+i*sin(y).