JTC1/SC22/WG14
N820
SC22/WG14 N820
Splitting Annex G
Clive D.W. Feather
[email protected]
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 <complex.h>, 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 <complex.h>
[#1] The macro
_Imaginary_I
is defined, and the macro
I
is defined to be _Imaginary_I (7.8).
GI.6 <tgmath.h>
[#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 <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;
/* 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 <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 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 <complex.h>
[#1] This subclause contains specification for the <complex.h>
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).