Ada '83 Quality and Style:
Guidelines for Professional Programmers
CHAPTER 7: Portability
A great deal of care was taken with the design of the Ada features related to
numeric computations to ensure that the language could be used in embedded
systems and mathematical applications where precision was important. As far as
possible, these features were made portable. However, there is an inevitable
tradeoff between maximally exploiting the available precision of numeric
computation on a particular machine and maximizing the portability of Ada
numeric constructs. This means that these Ada features, particularly numeric
types and expressions, must be used with great care if full portability of the
resulting program is to be guaranteed.
guideline
- Do not use the predefined numeric types in package
Standard
. Use range
and digits declarations and let the implementation do the derivation
implicitly from the predefined types.
- For programs that require greater accuracy than that provided by the
global assumptions, define a package that declares a private type and
operations as needed; see Pappas (1985) for a full explanation and examples.
example
The second and third examples below are not representable as subranges of
Integer
on a machine with a 16-bit word. The first example below allows a
compiler to choose a multiword representation if necessary.
Use
type Second_Of_Day is range 0 .. 86_400;
rather than
type Second_Of_Day is new Integer range 1 .. 86_400;
or
subtype Second_Of_Day is Integer range 1 .. 86_400;
rationale
An implementor is free to define the range of the predefined numeric types.
Porting code from an implementation with greater accuracy to one of lesser is
a time consuming and error-prone process. Many of the errors are not reported
until run-time.
This applies to more than just numerical computation. An easy-to-overlook
instance of this problem occurs if you neglect to use explicitly declared
types for integer discrete ranges (array sizes, loop ranges, etc.) (see
Guidelines 5.5.1
and 5.5.2).
If you do not provide an explicit type when specifying index
constraints and other discrete ranges, a predefined integer type is assumed.
exceptions
Any indexing into the predefined String type requires that the index at least
be a subtype of the predefined Integer type. The predefined packages also use
the various predefined types.
note
There is an alternative which this guideline permits.
As Guideline 7.1.5 suggests,
implementation dependencies can be encapsulated in packages intended for that
purpose. This could include the definition of a 32-bit integer type. It
would then be possible to derive additional types from that 32-bit type.
Language Ref Manual references:
3.5.4 Integer Types,
3.5.7 Floating Point Types,
3.5.9 Fixed Point Types,
8.6 The Package Standard,
C Predefined Language Environment,
F Implementation-Dependent Characteristics
guideline
- Know the Ada model for floating point types and arithmetic.
rationale
Declarations of Ada floating point types give users control over both the
representation and arithmetic used in floating point operations. Portable
properties of Ada programs are derived from the models for floating point
numbers of the subtype and the corresponding safe numbers. The relative
spacing and range of values in a type are determined by the declaration.
Attributes can be used to specify the transportable properties of an Ada
floating point type.
Language Ref Manual references:
3.5.7 Floating Point Types,
4.5.7 Accuracy of Operations with Real Operands
guideline
- Carefully analyze what accuracy and precision you really need.
rationale
Floating point calculations are done with the equivalent of the
implementation's predefined floating point types. The effect of extra "guard"
digits in internal computations can sometimes lower the number of digits that
must be specified in an Ada declaration. This may not be consistent over
implementations where the program is intended to be run. It may also lead to
the false conclusion that the declared types are sufficient for the accuracy
required.
The numeric type declarations should be chosen to satisfy the lowest precision
(smallest number of digits) that will provide the required accuracy. Careful
analysis will be necessary to show that the declarations are adequate.
Language Ref Manual references:
3.5.7 Floating Point Types,
4.5.7 Accuracy of Operations with Real Operands,
13.7.3 Representation Attributes of Real Types
guideline
- Do not press the accuracy limits of the machine(s).
rationale
The Ada floating point model is intended to facilitate program portability,
which is often at the expense of efficiency in using the underlying machine
arithmetic. Just because two different machines use the same number of digits
in the mantissa of a floating point number does not imply they will have the
same arithmetic properties. Some Ada implementations may give slightly better
accuracy than required by Ada because they make efficient use of the machine.
Do not write programs that depend on this.
Language Ref Manual references:
4.5.7 Accuracy of Operations with Real Operands,
F Implementation-Dependent Characteristics
guideline
- Comment the analysis and derivation of the numerical aspects of a
program.
rationale
Decisions and background about why certain precisions are required in a
program are important to program revision or porting. The underlying numerical
analysis leading to the program should be commented.
Language Ref Manual references:
2.7 Comments,
3.5.4 Integer Types,
3.5.6 Real Types,
4.5.7 Accuracy of Operations with Real Operands
guideline
- Use named numbers or universal real expressions rather than constants
of any particular type.
rationale
For a given radix (number base), there is a loss of accuracy for some rational
and all irrational numbers when represented by a finite sequence of digits.
Ada has named numbers and expressions of type universal_real that provide
maximal accuracy of representation in the source program. These numbers and
expressions are converted to finite representations at compile time. By using
universal real expressions and numbers, the programmer can automatically delay
the conversion to machine types until the point where it can be done with the
minimum loss of accuracy.
note
See also Guideline 3.2.5.
Language Ref Manual references:
2.4 Numeric Literals,
3.2 Objects and Named Numbers,
4.1 Universal Expressions,
4.9 Static Expressions and Static Subtypes
guideline
- Anticipate values of subexpressions to avoid exceeding the range of
their type. Use derived types, subtypes, factoring, and range constraints on
numeric types as described in Guidelines 3.4.1,
5.3.1,
and 5.5.3.
rationale
The Ada language does not require that an implementation perform range checks
on subexpressions within an expression. Even if the implementation on your
program's current target does not perform these checks, your program may be
ported to an implementation that does.
Language Ref Manual references:
3.3 Types and Subtypes,
3.3.2 Subtype Declarations,
3.4 Derived Types,
4.4 Expressions,
4.5 Operators and Expression Evaluation,
4.5.7 Accuracy of Operations with Real Operands
guideline
- Use
<=
and >=
to do relational tests on
real valued arguments, avoiding the <
, >
,
=
, and /=
operations.
- Use values of type attributes in comparisons and checking for small values.
example
The following examples test for (1) absolute "equality" in storage, (2)
absolute "equality" in computation, (3) relative "equality"
in storage, and (4) relative "equality" in computation.
abs (X - Y) <= Float_Type'Small -- (1)
abs (X - Y) <= Float_Type'Base'Small -- (2)
abs (X - Y) <= abs X * Float_Type'Epsilon -- (3)
abs (X - Y) <= abs X * Float_Type'Base'Epsilon -- (4) |
And specifically for "equality" to zero:
abs X <= Float_Type'Small -- (1)
abs X <= Float_Type'Base'Small -- (2)
abs X <= abs X * Float_Type'Epsilon -- (3)
abs X <= abs X * Float_Type'Base'Epsilon -- (4) |
rationale
Strict relational comparisons ( <
, >
,
=
, /=
) are a general problem in real
valued computations. Because of the way Ada comparisons are defined in terms
of model intervals, it is possible for the values of the Ada comparisons
A < B
and A = B
to depend on the implementation,
while A <= B
evaluates uniformly
across implementations. Note that for real values in Ada,
"A <= B
" is not the
same as "not (A > B)
".
Further explanation can be found in Cohen (1986) pp.227-233.
Type attributes are the primary means of symbolically accessing the
implementation of the Ada numeric model. When the characteristics of the model
numbers are accessed symbolically, the source code is portable. The
appropriate model numbers of any implementation will then be used by the
generated code.
Although zero is technically not a special case, it is often overlooked
because it looks like the simplest and, therefore, safest case. But in
reality, each time comparisons involve small values, evaluate the situation to
determine which technique is appropriate.
note
Regardless of language, real valued computations have inaccuracy. That the
corresponding mathematical operations have algebraic properties usually
introduces some confusion. This guideline explains how Ada deals with the
problem that most languages face.
Language Ref Manual references:
4.5.2 Relational Operators and Membership Tests
Back to document index