The features of Ada 9X that support numerically oriented applications are distributed over several parts of the (draft) Ada 9X reference manual [7]. Some of them are collected in the Numerics Annex, which, like the other special-needs annexes, is entirely optional; some appear in the core or in other mandatory annexes.
The basic concepts underlying the real (i.e., floating-point and fixed-point) types are covered in Chapters 3 and 4 of the core and will not be discussed here. These chapters no longer include the details of a model of real arithmetic or the accuracy requirements for predefined arithmetic operations based on the model. Indeed, there are no accuracy requirements, unless the Numerics Annex is supported. The model and the accuracy requirements are discussed there.
Included in Annex A (Core Language-Defined Attributes) are the definitions of the attributes of real types. Those that relate intimately to the accuracy of the predefined arithmetic operations are left implementation defined in Annex A and are revisited in the Numerics Annex.
Annex C (Predefined Language Environment) includes the specifications of a generic package of elementary functions and a package that defines types and operations related to random number generation. By virtue of their placement in Annex C, these packages must be provided by all implementations. This requirement is appropriate because of their utility in a wide variety of applications, including some that may not be numerically intensive. For example, square roots and logarithms are useful in encoding and decoding, and random numbers are indispensable to simulations.
A package defining Fortran-compatible types is included in Annex M (Interface to Other Languages), along with a discussion of the uses of the interfacing pragmas in a Fortran context. C and COBOL are also covered in this annex. Interface support for these languages is, of course, optional; if provided, it must conform with the specifications of this annex.
Annex K is the Numerics Annex. It introduces a pair of user-selectable modes, strict and relaxed, pertaining to the required accuracy of the predefined arithmetic operations. The model of real arithmetic and the accuracy requirements based on it apply only in the strict mode; no accuracy requirements apply in the relaxed mode, or if the Numerics Annex is not implemented. The Numerics Annex also specifies generic packages that define a complex type and arithmetic operations, complex elementary functions, and complex I/O operations, and it recommends that the Fortran and C interfaces be provided if those languages are widely supported in the target environment. Finally, it includes strict-mode requirements for the accuracy or performance of appropriate operations provided in the predefined numerical packages.
In the remainder of this chapter, we present an overview of the numerical features of Ada 9X. With one exception, we merely discuss the features, rather than displaying package specifications, etc. Readers needing further detail are urged to consult the draft reference manual and the rationale [8].
Some of the functions are overloaded. Thus, natural logarithms and logarithms to an aribtrary base are both provided; likewise, the trigonometric functions are provided both for the natural cycle of and for user-specified cycles.
The Ada 9X version of these functions is based on ISO/IEC DIS 11430 (for Ada 83), differing from it in two minor ways: the use of child packages for better hierarchical structuring, and the use of the unconstrained subtype of the generic formal parameter as the parameter and result types of all the functions. The latter improvement is intended to increase the chances of receiving a meaningful result from the evaluation of an elementary function reference when the generic formal parameter happens to be a subtype with range constraints. The Ada 9X feature underlying this improvement---use of the Base attribute other than in a prefix for another attribute---also benefits implementations of the elementary functions by allowing for the declaration of working variables with the precision, but not the range, of the generic formal parameter. In fact, implementations are no longer allowed to restrict the generic actual parameter to be an unconstrained subtype, as they are in the aforementioned draft international standard.
With the appropriate changes implied by the above discussion, the account of the proposed secondary standard [2] in the Ada Yearbook 1991 is relevant also to the version contained in Ada 9X. (Subsequent to the publication of the 1991 yearbook, the names of the formal parameters of the exponentiation operator were changed from X and Y to Left and Right so as to agree with the Ada 9X version, where the latter names are absolutely necessary. Also added was the explicit permission for implementations to restrict the generic actual parameter.)
package Ada.Numerics.Random_Numbers istype Generator is limited private;
Number_Of_Seed_Components : constant := [|em implementation-defined];
type Seed is array (1 .. Number_Of_Seed_Components) of Integer;
Seed_Error : exception;
subtype Uniformlly_Distributed is Float range 0.0 .. 1.0;function Random (Gen : Generator) return Uniformly_Distributed;
procedure Get_Seed (Gen : in Generator; Value : out Seed);
procedure Set_Seed (Gen : in Generator; Value : in Seed);
procedure Reset (Gen : in Generator; Initiator : in Integer);
procedure Reset (Gen : in Generator);
private
-- [|em not specified]
end Ada.Numerics.Random_Numbers;
Objects of type Generator represent generators of random numbers represented in floating point and uniformly distributed on the semi-open interval [0.0, 1.0). One obtains the ``next'' random number from the sequence produced by a given generator by invoking the Random function with that generator as the actual parameter. In the simplest applications, nothing more is required; the rest of the package provides a variety of services that may be needed by advanced applications.
The repeatable behavior expected during program development is obtained by default. Once a program becomes operational, unique sequences of random numbers can be obtained in each run by invoking the Reset operation on a generator before using it to generate any random numbers. When invoked without an Initiator parameter, this operation resets the state of a generator to one that depends in an implementation-dependent way on the current date and time.
Tasking applications that require separate sequences of random numbers in each of several tasks can declare generator objects in the relevant tasks. In this case, separate sequences are obtained by performing the Reset operation ( with a unique integer-valued Initiator parameter) on the generator in each task. By using the same set of initiator values on subsequent runs, the same set of separate sequences will be repeated. Resetting with an initiator is defined to change the state of the generator to one that depends in an implementation-dependent way on the value of the initiator. The specifications provide that, if the generator period is long enough, each initiator value shall initiate a distinct subsequence of the full period that does not overlap with any other such subsequence in a practical sense; if that is not possible, then the resulting state is, at least, a rapidly varying function of the initiator value.
It is customary, when multiple generators are provided, to equate a generator with its ``seed''---that is, its changeable state. A different approach has been chosen for Ada 9X, however, so as to allow the Random operation to be realized as a function and to make it impossible to corrupt the state between calls to Random (or, alternatively, to make it unnecessary to validate the state at each such call, in case it has been corrupted). These benefits are achieved partly by making the Generator type private. Since the mode of the generator parameter in all the operations on generators is in, a level of indirection is required to access the actual state information; consequently, the Generator type is limited in addition to private.
Nevertheless, there is a need in some applications to obtain and maybe even set the state of a generator. For example, a long run may be checkpointed and restarted later. Or, it might be desirable to know the actual state of a generator just prior to the appearance of unexplained behavior. Finally, for experimentation it might be desirable to force the generator into a given state. For all these purposes, we provide also a type called Seed whose values represent actual generator states. The Get_Seed operation obtains the current state of a given generator and delivers it as a value of type Seed; Set_Seed changes the state of a given generator to that corresponding to the seed value supplied. The type Seed is defined as an array of integers for portability, the number of them being given by the named number Number_Of_Seed_Components. This does not restrict the implementation of generators to use the same representation internally; the only requirement is that each distinct internal state must be capable of being represented as a unique vector of integers, the mapping being completely implementation defined. (It is possible that some values of the type Seed will not correspond to generator states. If such a value is passed to Set_Seed, that operation will raise the Seed_Error exception. Note that calls to Set_Seed are the only places where validation of the generator state is required. Such calls will be infrequent or absent.)
We do not prescribe a particular random number generation algorithm, the intent being to allow any respectable algorithm to be used. The Numerics Annex does specify some fairly stringent performance requirements for the strict mode, but these will be satisfied by good examples of congruential generators [10] (for which the internal state is usually a single integer or floating-point number), the phenomenally long period Fibonacci generators [9] (for which the internal state is usually a modest array of integer or floating-point components, plus a carry or borrow bit), or combination generators [4, 11] (for which the internal state is usually a small array of integers). Since no one algorithm is best for all applications, implementations are required to document the algorithm used, so that the user can judge its suitability for the application at hand.
Several features of the complex types package in Ada 9X are somewhat unconventional:
Ada.Numerics.Generic_Complex_Types is based on the generic package of complex types and arithmetic operations proposed for standardization (for Ada 83) by the WG9 Numerics Rapporteur Group.The proposed secondary standards for complex arithmetic were changed in 1993 to maintain compatibility with the versions included in Ada 9X. Thus, the rationale [6] published in the Ada Yearbook 1993 is now somewhat out of date.
This generic package has a generic formal package parameter. It is intended to be instantiated with an instance of .
The form of output to a file is that of parenthesized aggregate notation, with the real and imaginary parts both formatted according to the specified or defaulted values of Fore, Aft, and Exp. Output to a string is similar, but it uses a value of zero for the Fore of the real part and a value for the Fore of the imaginary part that completely fills the string. The effect is that the left parenthesis, real part, and comma are left justified in the given string, while the imaginary part and right parenthesis are right justified. This behavior results in optimal use of the available space when the real and imaginary parts of the value to be transmitted have widely disparate magnitudes.
When reading from a file specifies a Width of zero, the expected form of the input is parenthesized aggregate notation, with blanks, line terminators, and page terminators freely allowed before each of its components. When the Width is nonzero, exactly Width characters, or the characters up to the end of the current line, whichever comes first, are consumed; the complex value may be punctuated in parenthesized aggregate notation, or the parentheses and comma may be omitted, provided that the real and imaginary parts are separated by at least one blank. The latter form is allowed (with a nonzero Width) for compatibility with existing data files produced by Fortran programs.
The combination of program pieces written in diverse languages has been difficult in the past because each language (and sometimes each language processor) has its own conventions regarding the mapping of data, the addressing of parameters, etc. The purpose of the Fortran interface is to facilitate the creation of such combinations. To this end, the predefined child package Ada.Interfaces.Fortran defines a set of types, analogous to the Fortran intrinsic types, that are intended to have the same representation they have in Fortran. Objects of these types can therefore be passed directly to, or received from, Fortran subprograms seamlessly.
Along with the interface package, Fortran interface support includes implementation of a convention identifier of Fortran in the Import and Export pragmas and, for the specified kinds of types, in the Convention pragma. The Import and Export pragmas arrange for the appropriate link name to be used for an external reference, and they cause the parameter addressing conventions of the foreign language to be observed. They can also be used to equate a Fortran named common block with an object of an appropriate Ada record type declared in a library package. The Convention pragma would need to be specified for the type of such a record object, to guarantee that it is mapped the same as in Fortran. The latter pragma can also be used to indicate that objects of a record type passed to, or received from, a Fortran 90 routine should be mapped as in Fortran 90 and that an access type should be represented as the corresponding pointer type in Fortran 90.
If several implementations of Fortran are provided in the target environment, there can be a uniquely named child package of Ada.Interfaces corresponding to each, along with a corresponding convention identifier for use in the interfacing pragmas. A Fortran interface package can declare types in addition to those required by the language definition. For example, an interface package for an implementation of Fortran 77 might declare additional types named Integer_Star_2, Integer_Star_4, Logical_Star_1, etc., while one for an implementation of Fortran 90 might declare types like Real_Kind_0, Real_Kind_1, Character_Kind_1, etc., to match the Fortran types Integer*2, Character (Kind=1), and so forth.
The language does not say how the user shall select a mode, or which mode shall be the default, but it is clear that the mode must govern both code generation and library searches. The modes need not actually be distinct, however; an implementation that meets the requirements of the strict mode at little or no cost to the user has no real incentive to offer the user another choice, and the two modes might be identical.
The most significant changes in the model are as follows:
At the same time, the accuracy requirements were difficult enough to satisfy, for some combinations of source and target smalls, that many vendors prohibited representation clauses for nonbinary smalls, or perhaps for smalls not decomposable as products of powers of two and five. Algorithms for arbitrary smalls that made only modest assumptions about the hardware were published [5] but were never widely implemented.
In Ada 9X, we have abandoned the application of the model of real arithmetic to fixed-point types. There no longer are model numbers, safe numbers, and machine numbers of fixed-point types. The values of a fixed-point type are, simply, integer multiples of the type's small that lie within a certain range. An arithmetic operation that yields a result of a fixed-point type delivers one of these multiples. The accuracy requirements are expressed in terms of sets of these multiples called ``perfect result sets'' and ``close result sets.'' The former case applies when the source and target smalls are ``compatible'' (related in a commensurate way typical of many carefully constructed applications); the perfect result set in many instances contains a single member, and at worst contains a pair of adjacent values, implying at most one rounding error. Close result sets are used when the smalls are incompatible; in this case, the result sets are small but implementation-defined sets of adjacent integer multiples of the type's small containing the perfect result set as a subset. It is hoped that the broader implementation freedoms permitted in the latter case will foster wider support for fixed-point types with arbitrary smalls.
Other changes involving fixed-point types are as follows:
These error bounds are superseded by requirements that certain functions must produce exact results for particular arguments, or results within the smallest model interval containing the exact result (when the exact result is not a model number). Unlike the error bounds, these prescribed results apply even in the relaxed mode.
Numerous changes have taken place in these attributes in Ada 9X. As an example, note that T'Mantissa gives the number of binary digits in the mantissa of the model numbers of the type T in Ada 83. Since the model numbers of Ada 9X have the hardware radix, the analogous attribute there no longer necessarily refers to binary digits. On hexadecimal hardware, for instance, the corresponding attribute in Ada 9X would be expected to yield only one-fourth the value that T'Mantissa gives. (In reality, the ratio is not exactly one-fourth, since the value of the Ada 83 attribute is necessarily selected from a quantized set and is not generally a multiple of four.) To avoid problems of upwards inconsistency, in which Ada 83 programs give different results in Ada 9X, we have given this attribute a new name, Model_Mantissa. The old attribute, Mantissa, is no longer defined by the language, which of course poses a problem of a different kind, classed as an upwards incompatibility (in which valid Ada 83 programs no longer compile successfully). To ameliorate this problem, we have recommended that implementations continue to provide the obsolescent attributes as implementation-defined attributes for an appropriate transition period; they should recognize the old attributes and allow them, but produce a warning message. Uses of the old attributes can be adjusted on a case-by-case basis to use the new attributes instead.
Like Mantissa, the Emin attribute also changes its value by a factor of about four in Ada 9X, and it has consequently been renamed Model_Emin. On the other hand, an attribute like Epsilon changes its value little, if at all. It is defined in terms of the radix and mantissa length of model numbers, and although these are both changing radically, their changes complement each other. If Epsilon changes at all, it is due to the fact that mantissa lengths are no longer quantized. Nevertheless, because its value might change, this attribute has also been renamed (as Model_Epsilon).
Several of the model-related attributes of floating-point types have been omitted without replacement, and there are a small number of new attributes. Many of the model-related attributes of fixed-point types have been omitted as well, since fixed-point arithmetic no longer uses the concept of model numbers for the basis of its accuracy requirements.
These attributes are all presented in Annex A, along with other attributes of real types that do not relate to the model. The model-related attributes have implementation-defined meanings and values if the Numerics Annex is not implemented, or if the relaxed mode is in effect. Their definition in terms of the model is given in the Numerics Annex.
In the aforementioned draft international standard, these attributes are represented as subprograms in a generic package. In order to avoid conflicts with other attributes of Ada 9X, we have changed some of their names, and there are slight differences in the complement of functions provided. With allowance for these changes, the account [3] of the proposed secondary standard in the Ada Yearbook 1992 is also relevant to the version of the primitive functions in Ada 9X. (Subsequent to the publication of the 1992 yearbook, the proposed secondary standard for the primitive functions was also changed slightly in response to public comment.)
[2] K. W. Dritz. Towards an Ada Standard for the Elementary Functions. F. Long (ed.), Ada Yearbook 1991, Chapman and Hall, London, 1991, pp. 283--290.
[3] K. W. Dritz. Development of an Ada Standard for Primitive Floating-Point Functions. F. Long (ed.), Ada Yearbook 1992, Chapman and Hall, London, 1992, pp. 295--302.
[4] P. L'Ecuyer. Efficient and Portable Combined Random Number Generators. CACM 31(6):742--749,774, June 1988.
[5] P. N. Hilfinger. Implementing Ada Fixed-Point Types Having Arbitrary Scales. Computer Science Report No. 582, Univ. of California at Berkeley, June 1990.
[6] G. S. Hodgson. The Developing Standards for Complex Scalar and Array Types, Basic Operations, and Elementary Functions for Ada. C. Loftus (ed.), Ada Yearbook 1993, IOS Press, Amsterdam, 1993, pp. 369--382.
[7] ISO/IEC CD 8652. Programming Languages---Ada. Committee Draft, September 1993.
[8] ISO/IEC JTC1/SC22 N 1455. Programming Languages---Rationale for the Programming Language Ada. Draft version 4.0, September 1993.
[9] G. Marsaglia and A. Zaman. A New Class of Random Number Generators. Annals of Applied Probability 1(3):462--480, August 1991.
[10] S. K. Park and K. W. Miller. Random Number Generators: Good Ones are Hard to Find. CACM 13(10):1192--1201, October 1988.
[11] B. A. Wichmann and I. D. Hill. A Pseudo-Random Number Generator. Report DITC 6/82, National Physical Laboratory, Teddington, England, June 1982.