G.1.1 Complex Types
Static Semantics
The generic library
package Numerics.Generic_Complex_Types has the following declaration:
type Complex
is
record
Re, Im : Real'Base;
end record;
i :
constant Imaginary;
j :
constant Imaginary;
function Re (X : Complex)
return Real'Base;
function Im (X : Complex)
return Real'Base;
function Im (X : Imaginary)
return Real'Base;
procedure Set_Re (X :
in out Complex;
Re :
in Real'Base);
procedure Set_Im (X :
in out Complex;
Im :
in Real'Base);
procedure Set_Im (X :
out Imaginary;
Im :
in Real'Base);
function Compose_From_Cartesian (Re, Im : Real'Base)
return Complex;
function Compose_From_Cartesian (Re : Real'Base)
return Complex;
function Compose_From_Cartesian (Im : Imaginary)
return Complex;
function Modulus (X : Complex)
return Real'Base;
function "
abs" (Right : Complex)
return Real'Base
renames Modulus;
function Argument (X : Complex)
return Real'Base;
function Argument (X : Complex;
Cycle : Real'Base)
return Real'Base;
function Compose_From_Polar (Modulus, Argument : Real'Base)
return Complex;
function Compose_From_Polar (Modulus, Argument, Cycle : Real'Base)
return Complex;
function "+" (Right : Complex)
return Complex;
function "-" (Right : Complex)
return Complex;
function Conjugate (X : Complex)
return Complex;
function "+" (Left, Right : Complex) return Complex;
function "-" (Left, Right : Complex) return Complex;
function "*" (Left, Right : Complex) return Complex;
function "/" (Left, Right : Complex) return Complex;
function "**" (Left : Complex; Right : Integer) return Complex;
function "+" (Right : Imaginary)
return Imaginary;
function "-" (Right : Imaginary)
return Imaginary;
function Conjugate (X : Imaginary)
return Imaginary
renames "-";
function "
abs" (Right : Imaginary)
return Real'Base;
function "+" (Left, Right : Imaginary) return Imaginary;
function "-" (Left, Right : Imaginary) return Imaginary;
function "*" (Left, Right : Imaginary) return Real'Base;
function "/" (Left, Right : Imaginary) return Real'Base;
function "**" (Left : Imaginary; Right : Integer) return Complex;
function "<" (Left, Right : Imaginary) return Boolean;
function "<=" (Left, Right : Imaginary) return Boolean;
function ">" (Left, Right : Imaginary) return Boolean;
function ">=" (Left, Right : Imaginary) return Boolean;
function "+" (Left : Complex; Right : Real'Base) return Complex;
function "+" (Left : Real'Base; Right : Complex) return Complex;
function "-" (Left : Complex; Right : Real'Base) return Complex;
function "-" (Left : Real'Base; Right : Complex) return Complex;
function "*" (Left : Complex; Right : Real'Base) return Complex;
function "*" (Left : Real'Base; Right : Complex) return Complex;
function "/" (Left : Complex; Right : Real'Base) return Complex;
function "/" (Left : Real'Base; Right : Complex) return Complex;
function "+" (Left : Complex; Right : Imaginary) return Complex;
function "+" (Left : Imaginary; Right : Complex) return Complex;
function "-" (Left : Complex; Right : Imaginary) return Complex;
function "-" (Left : Imaginary; Right : Complex) return Complex;
function "*" (Left : Complex; Right : Imaginary) return Complex;
function "*" (Left : Imaginary; Right : Complex) return Complex;
function "/" (Left : Complex; Right : Imaginary) return Complex;
function "/" (Left : Imaginary; Right : Complex) return Complex;
function "+" (Left : Imaginary; Right : Real'Base) return Complex;
function "+" (Left : Real'Base; Right : Imaginary) return Complex;
function "-" (Left : Imaginary; Right : Real'Base) return Complex;
function "-" (Left : Real'Base; Right : Imaginary) return Complex;
function "*" (Left : Imaginary; Right : Real'Base) return Imaginary;
function "*" (Left : Real'Base; Right : Imaginary) return Imaginary;
function "/" (Left : Imaginary; Right : Real'Base) return Imaginary;
function "/" (Left : Real'Base; Right : Imaginary) return Imaginary;
private
type Imaginary is new Real'Base;
i : constant Imaginary := 1.0;
j : constant Imaginary := 1.0;
end Ada.Numerics.Generic_Complex_Types;
{
8652/0020}
{
AI95-00126-01}
The library package Numerics.Complex_Types is declared
pure and defines the same types, constants, and subprograms as Numerics.Generic_Complex_Types,
except that the predefined type Float is systematically substituted for
Real'Base throughout. Nongeneric equivalents of Numerics.Generic_Complex_Types
for each of the other predefined floating point types are defined similarly,
with the names Numerics.Short_Complex_Types, Numerics.Long_Complex_Types,
etc.
Reason: The nongeneric equivalents are
provided to allow the programmer to construct simple mathematical applications
without being required to understand and use generics.
Reason: The nongeneric equivalents all
export the types Complex and Imaginary and the constants i and j (rather
than uniquely named types and constants, such as Short_Complex, Long_Complex,
etc.) to preserve their equivalence to actual instantiations of the generic
package and to allow the programmer to change the precision of an application
globally by changing a single context clause.
Ramification: {
AI12-0241-1}
A pure unit is always nonblocking, so we don't need to talk about the
Nonblocking aspect of these packages. Similarly, a pure unit always has
the Global aspect set to
null, so we don't need to talk about
that, either.
{
AI95-00434-01}
[Complex is a visible type with Cartesian components.]
Reason: The Cartesian representation
is far more common than the polar representation, in practice. The accuracy
of the results of the complex arithmetic operations and of the complex
elementary functions is dependent on the representation; thus, implementers
need to know that representation. The type is visible so that complex
“literals” can be written in aggregate notation, if desired.
[Imaginary is a private type; its full type is derived
from Real'Base.]
Reason: The
Imaginary type and the constants i and j are provided for two reasons:
They allow complex “literals”
to be written in the alternate form of a
+ b*i (or a
+ b*j), if desired. Of course, in some
contexts the sum will need to be parenthesized.
{
AI12-0437-1}
{
AI12-0453-1}
When an Ada binding to ISO/IEC 60559:2020 that provides (signed) infinities
as the result of operations that overflow becomes available, it will
be important to allow arithmetic between pure-imaginary and complex operands
without requiring the former to be represented as (or promoted to) complex
values with a real component of zero. For example, the multiplication
of
a +
b*i
by
d*i should yield –
b·
d +
a·
d*i, but if one cannot avoid representing
the pure-imaginary value
d*i as the
complex value 0.0 +
d*i, then a NaN
("Not-a-Number") could be produced as the result of multiplying
a by 0.0 (for example, when
a
is infinite); the NaN could later trigger an exception. Providing the
Imaginary type and overloadings of the arithmetic operators for mixtures
of Imaginary and Complex operands gives the programmer the same control
over avoiding premature coercion of pure-imaginary values to complex
as is already provided for pure-real values.
Reason: The
Imaginary type is private, rather than being visibly derived from Real'Base,
for two reasons:
to preclude implicit conversions of real literals
to the Imaginary type (such implicit conversions would make many common
arithmetic expressions ambiguous); and
to suppress the implicit derivation of the
multiplication, division, and absolute value operators with Imaginary
operands and an Imaginary result (the result type would be incorrect).
Reason: {
AI12-0453-1}
The base subtype Real'Base is used for the component type of Complex,
the parent type of Imaginary, and the parameter and result types of some
of the subprograms to maximize the chances of being able to pass meaningful
values into the subprograms and receive meaningful results back. The
generic formal parameter Real therefore plays only one role, that of
providing the precision to be maintained in complex arithmetic calculations.
Thus, the subprograms in Numerics.Generic_Complex_Types share with those
in Numerics.Generic_Elementary_Functions, and indeed even with the predefined
arithmetic operations (see
4.5), the property
of being free of range checks on input and output, that is, of being
able to exploit the base range of the relevant floating point type fully.
As a result, the user loses the ability to impose application-oriented
bounds on the range of values that the components of a complex variable
can acquire; however, it can be argued that few, if any, applications
have a naturally square domain (as opposed to a circular domain) anyway.
The arithmetic operations
and the Re, Im, Modulus, Argument, and Conjugate functions have their
usual mathematical meanings. When applied to a parameter of pure-imaginary
type, the “imaginary-part” function Im yields the value of
its parameter, as the corresponding real value. The remaining subprograms
have the following meanings:
Reason: The middle case can be understood
by considering the parameter of pure-imaginary type to represent a complex
value with a zero real part.
The Set_Re and Set_Im procedures replace the designated
component of a complex parameter with the given real value; applied to
a parameter of pure-imaginary type, the Set_Im procedure replaces the
value of that parameter with the imaginary value corresponding to the
given real value.
The Compose_From_Cartesian function constructs
a complex value from the given real and imaginary components. If only
one component is given, the other component is implicitly zero.
The Compose_From_Polar function constructs a complex
value from the given modulus (radius) and argument (angle). When the
value of the parameter Modulus is positive (resp., negative), the result
is the complex value represented by the point in the complex plane lying
at a distance from the origin given by the absolute value of Modulus
and forming an angle measured counterclockwise from the positive (resp.,
negative) real axis given by the value of the parameter Argument.
When the Cycle parameter is specified, the result
of the Argument function and the parameter Argument of the Compose_From_Polar
function are measured in units such that a full cycle of revolution has
the given value; otherwise, they are measured in radians.
The computed results
of the mathematically multivalued functions are rendered single-valued
by the following conventions, which are meant to imply the principal
branch:
The result of the Modulus function is nonnegative.
The result of the Argument function is in the quadrant
containing the point in the complex plane represented by the parameter
X. This may be any quadrant (I through IV); thus, the range of the Argument
function is approximately –π to π (–Cycle/2.0
to Cycle/2.0, if the parameter Cycle is specified).
When the point represented by the parameter X lies on the negative real
axis, the result approximates
π (resp., –π) when the
sign of the imaginary component of X is positive (resp., negative), if
Real'Signed_Zeros is True;
π, if Real'Signed_Zeros is False.
Because a result lying on or near one of the axes
may not be exactly representable, the approximation inherent in computing
the result may place it in an adjacent quadrant, close to but on the
wrong side of the axis.
Dynamic Semantics
The exception Numerics.Argument_Error is raised by
the Argument and Compose_From_Polar functions with specified cycle, signaling
a parameter value outside the domain of the corresponding mathematical
function, when the value of the parameter Cycle is zero or negative.
The
exception Constraint_Error is raised by the division operator when the
value of the right operand is zero, and by the exponentiation operator
when the value of the left operand is zero and the value of the exponent
is negative, provided that Real'Machine_Overflows is True; when Real'Machine_Overflows
is False, the result is unspecified.
[Constraint_Error
can also be raised when a finite result overflows (see
G.2.6).]
Discussion: {
AI12-0437-1}
It is anticipated that an Ada binding to ISO/IEC 60559:2020 will be developed
in the future. As part of such a binding, the Machine_Overflows attribute
of a conformant floating point type will be specified to yield False,
which will permit implementations of the complex arithmetic operations
to deliver results with an infinite component (and set the overflow flag
defined by the binding) instead of raising Constraint_Error in overflow
situations, when traps are disabled. Similarly, it is appropriate for
the complex arithmetic operations to deliver results with infinite components
(and set the zero-divide flag defined by the binding) instead of raising
Constraint_Error in the situations defined above, when traps are disabled.
Finally, such a binding should also specify the behavior of the complex
arithmetic operations, when sensible, given operands with infinite components.
Implementation Requirements
In the implementation of Numerics.Generic_Complex_Types,
the range of intermediate values allowed during the calculation of a
final result shall not be affected by any range constraint of the subtype
Real.
Implementation Note: Implementations
of Numerics.Generic_Complex_Types written in Ada should therefore avoid
declaring local variables of subtype Real; the subtype Real'Base should
be used instead.
In
the following cases, evaluation of a complex arithmetic operation shall
yield the
prescribed result, provided that the preceding rules
do not call for an exception to be raised:
The results of the Re, Im, and Compose_From_Cartesian
functions are exact.
The real (resp., imaginary) component of the result
of a binary addition operator that yields a result of complex type is
exact when either of its operands is of pure-imaginary (resp., real)
type.
Ramification: The result of the addition
operator is exact when one of its operands is of real type and the other
is of pure-imaginary type. In this particular case, the operator is analogous
to the Compose_From_Cartesian function; it performs no arithmetic.
The real (resp., imaginary) component of the result
of a binary subtraction operator that yields a result of complex type
is exact when its right operand is of pure-imaginary (resp., real) type.
The real component of the result of the Conjugate
function for the complex type is exact.
When the point in the complex plane represented
by the parameter X lies on the nonnegative real axis, the Argument function
yields a result of zero.
Discussion: Argument(X + i*Y) is analogous
to EF.Arctan(Y, X), where EF is an appropriate instance
of Numerics.Generic_Elementary_Functions, except when X and Y are both
zero, in which case the former yields the value zero while the latter
raises Numerics.Argument_Error.
When the value of the parameter Modulus is zero,
the Compose_From_Polar function yields a result of zero.
When the value of the parameter Argument is equal
to a multiple of the quarter cycle, the result of the Compose_From_Polar
function with specified cycle lies on one of the axes. In this case,
one of its components is zero, and the other has the magnitude of the
parameter Modulus.
Exponentiation by a zero exponent yields the value
one. Exponentiation by a unit exponent yields the value of the left operand.
Exponentiation of the value one yields the value one. Exponentiation
of the value zero yields the value zero, provided that the exponent is
nonzero. When the left operand is of pure-imaginary type, one component
of the result of the exponentiation operator is zero.
When the result, or a result component, of any operator
of Numerics.Generic_Complex_Types has a mathematical definition in terms
of a single arithmetic or relational operation, that result or result
component exhibits the accuracy of the corresponding operation of the
type Real.
Other accuracy requirements for the Modulus, Argument,
and Compose_From_Polar functions, and accuracy requirements for the multiplication
of a pair of complex operands or for division by a complex operand, all
of which apply only in the strict mode, are given in
G.2.6.
The sign of a zero result or zero result component
yielded by a complex arithmetic operation or function is implementation
defined when Real'Signed_Zeros is True.
Implementation defined: The sign of a
zero result (or a component thereof) from any operator or function in
Numerics.Generic_Complex_Types, when Real'Signed_Zeros is True.
Implementation Permissions
{
AI12-0444-1}
The nongeneric equivalent packages can be actual instantiations of the
generic package for the appropriate predefined type, though that is not
required.
{
8652/0091}
{
AI95-00434-01}
Implementations may obtain the result of exponentiation of a complex
or pure-imaginary operand by repeated complex multiplication, with arbitrary
association of the factors and with a possible final complex reciprocation
(when the exponent is negative). Implementations are also permitted to
obtain the result of exponentiation of a complex operand, but not of
a pure-imaginary operand, by converting the left operand to a polar representation;
exponentiating the modulus by the given exponent; multiplying the argument
by the given exponent; and reconverting to a Cartesian representation.
Because of this implementation freedom, no accuracy requirement is imposed
on complex exponentiation (except for the prescribed results given above,
which apply regardless of the implementation method chosen).
Implementation Advice
{
AI12-0437-1}
Because the usual mathematical meaning of multiplication of a complex
operand and a real operand is that of the scaling of both components
of the former by the latter, an implementation should not perform this
operation by first promoting the real operand to complex type and then
performing a full complex multiplication. In systems that, in the future,
support an Ada binding to ISO/IEC 60559:2020, the latter technique will
not generate the required result when one of the components of the complex
operand is infinite. (Explicit multiplication of the infinite component
by the zero component obtained during promotion yields a NaN that propagates
into the final result.) Analogous advice applies in the case of multiplication
of a complex operand and a pure-imaginary operand, and in the case of
division of a complex operand by a real or pure-imaginary operand.
Implementation Advice: Mixed real and
complex operations (as well as pure-imaginary and complex operations)
should not be performed by converting the real (resp. pure-imaginary)
operand to complex.
{
AI12-0437-1}
Likewise, because the usual mathematical meaning of addition of a complex
operand and a real operand is that the imaginary operand remains unchanged,
an implementation should not perform this operation by first promoting
the real operand to complex type and then performing a full complex addition.
In implementations in which the Signed_Zeros attribute of the component
type is True (and which therefore conform to ISO/IEC 60559:2020 in regard
to the handling of the sign of zero in predefined arithmetic operations),
the latter technique will not generate the required result when the imaginary
component of the complex operand is a negatively signed zero. (Explicit
addition of the negative zero to the zero obtained during promotion yields
a positive zero.) Analogous advice applies in the case of addition of
a complex operand and a pure-imaginary operand, and in the case of subtraction
of a complex operand and a real or pure-imaginary operand.
Implementations in which Real'Signed_Zeros is True
should attempt to provide a rational treatment of the signs of zero results
and result components. As one example, the result of the Argument function
should have the sign of the imaginary component of the parameter X when
the point represented by that parameter lies on the positive real axis;
as another, the sign of the imaginary component of the Compose_From_Polar
function should be the same as (resp., the opposite of) that of the Argument
parameter when that parameter has a value of zero and the Modulus parameter
has a nonnegative (resp., negative) value.
Implementation Advice: If Real'Signed_Zeros
is True for Numerics.Generic_Complex_Types, a rational treatment of the
signs of zero results and result components should be provided.
Wording Changes from Ada 83
The semantics
of Numerics.Generic_Complex_Types differs from Generic_Complex_Types
as defined in ISO/IEC CD 13813 (for Ada 83) in the following ways:
The generic package is a child of the package
defining the Argument_Error exception.
The nongeneric equivalents export types and
constants with the same names as those exported by the generic package,
rather than with names unique to the package.
Implementations are not allowed to impose
an optional restriction that the generic actual parameter associated
with Real be unconstrained. (In view of the ability to declare variables
of subtype Real'Base in implementations of Numerics.Generic_Complex_Types,
this flexibility is no longer needed.)
The dependence of the Argument function on
the sign of a zero parameter component is tied to the value of Real'Signed_Zeros.
Conformance to accuracy requirements is conditional.
Extensions to Ada 95
{
AI95-00161-01}
Amendment Correction: Added a
pragma
Preelaborable_Initialization to type Imaginary, so that it can be used
in preelaborated units.
Wording Changes from Ada 95
{
8652/0020}
{
AI95-00126-01}
Corrigendum: Explicitly stated that the nongeneric equivalents
of Generic_Complex_Types are pure.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe