3.5.4 Integer Types
An
integer_type_definition
defines an integer type; it defines either a
signed integer type,
or a
modular integer type. The base range of a signed integer
type includes at least the values of the specified range. A modular type
is an integer type with all arithmetic modulo a specified positive
modulus;
such a type corresponds to an unsigned type with wrap-around semantics.
Syntax
Discussion: We don't call this a
range_constraint,
because it is rather different — not only is it required to be
static, but the associated overload resolution rules are different than
for normal range constraints. A similar comment applies to
real_range_specification.
This used to be
integer_range_specification
but when we added support for modular types, it seemed overkill to have
three levels of syntax rules, and just calling these
signed_integer_range_specification
and
modular_range_specification loses the
fact that they are defining different classes of types, which is important
for the generic type matching rules.
Name Resolution Rules
Legality Rules
The
expression
of a
modular_type_definition
shall be static, and its value (the
modulus) shall be positive,
and shall be no greater than System.Max_Binary_Modulus if a power of
2, or no greater than System.Max_Nonbinary_Modulus if not.
Reason: For a 2's-complement machine,
supporting nonbinary moduli greater than System.Max_Int can be quite
difficult, whereas essentially any binary moduli are straightforward
to support, up to 2*System.Max_Int+2, so this justifies having two separate
limits.
Static Semantics
The set of values for a signed integer type is the
(infinite) set of mathematical integers[, though only values of the base
range of the type are fully supported for run-time operations]. The set
of values for a modular integer type are the values from 0 to one less
than the modulus, inclusive.
Implementation Note: {
AI95-00114-01}
The base range of a signed integer type might be much larger than is
necessary to satisfy the above requirements.
To be honest: The conversion mentioned
above is not an
implicit subtype conversion (which is something
that happens at overload resolution, see
4.6),
although it happens implicitly. Therefore, the freezing rules are not
invoked on the type (which is important so that representation items
can be given for the type).
A
modular_type_definition
defines a modular type whose base range is from zero to one less than
the given modulus.
A
modular_type_definition
also defines a constrained first subtype of the type with a range that
is the same as the base range of the type.
There is a predefined signed
integer subtype named Integer[, declared in the visible part of package
Standard]. It is constrained to the base range of its type.
Reason: Integer is a constrained subtype,
rather than an unconstrained subtype. This means that on assignment to
an object of subtype Integer, a range check is required. On the other
hand, an object of subtype Integer'Base is unconstrained, and no range
check (only overflow check) is required on assignment. For example, if
the object is held in an extended-length register, its value might be
outside of Integer'First .. Integer'Last. All parameter and result subtypes
of the predefined integer operators are of such unconstrained subtypes,
allowing extended-length registers to be used as operands or for the
result. In an earlier version of Ada 95, Integer was unconstrained. However,
the fact that certain Constraint_Errors might be omitted or appear elsewhere
was felt to be an undesirable upward inconsistency in this case. Note
that for Float, the opposite conclusion was reached, partly because of
the high cost of performing range checks when not actually necessary.
Objects of subtype Float are unconstrained, and no range checks, only
overflow checks, are performed for them.
Integer
has two predefined subtypes, [declared in the visible part of package
Standard:]
subtype Natural is Integer range 0 .. Integer'Last;
subtype Positive is Integer range 1 .. Integer'Last;
A
type defined by an
integer_type_definition
is implicitly derived from
root_integer, an anonymous predefined
(specific) integer type, whose base range is System.Min_Int .. System.Max_Int.
However, the base range of the new type is not inherited from
root_integer,
but is instead determined by the range or modulus specified by the
integer_type_definition.
[Integer literals are all of
the type
universal_integer, the universal type (see
3.4.1)
for the class rooted at
root_integer, allowing their use with
the operations of any integer type.]
Discussion: This implicit derivation
is not considered exactly equivalent to explicit derivation via a
derived_type_definition.
In particular, integer types defined via a
derived_type_definition
inherit their base range from their parent type. A type defined by an
integer_type_definition
does not necessarily inherit its base range from
root_integer.
It is not specified whether the implicit derivation from
root_integer
is direct or indirect, not that it really matters. All we want is for
all integer types to be descendants of
root_integer.
Implementation Note: It is the intent
that even nonstandard integer types (see below) will be descendants of
root_integer, even though they might have a base range that exceeds
that of
root_integer. This causes no problem for static calculations,
which are performed without range restrictions (see
4.9).
However for run-time calculations, it is possible that Constraint_Error
might be raised when using an operator of
root_integer on the
result of 'Val applied to a value of a nonstandard integer type.
The
position number of
an integer value is equal to the value.
{
AI95-00340-01}
For every modular subtype S, the following attributes are defined:
S'Mod
{
AI95-00340-01}
S'Mod denotes a function with the following specification:
function S'Mod (Arg : universal_integer)
return S'Base
This function returns Arg mod
S'Modulus, as a value of the type of S.
S'Modulus
S'Modulus yields the modulus
of the type of S, as a value of the type
universal_integer.
Dynamic Semantics
For a modular type, if the result of the execution
of a predefined operator (see
4.5) is outside
the base range of the type, the result is reduced modulo the modulus
of the type to a value that is within the base range of the type.
For
a signed integer type, the exception Constraint_Error is raised by the
execution of an operation that cannot deliver the correct result because
it is outside the base range of the type. [
For any integer type, Constraint_Error is raised
by the operators "/", "
rem", and "
mod"
if the right operand is zero.]
Implementation Requirements
In an implementation, the range
of Integer shall include the range –2**15+1 .. +2**15–1.
If Long_Integer is predefined
for an implementation, then its range shall include the range –2**31+1
.. +2**31–1.
System.Max_Binary_Modulus shall be at least 2**16.
Implementation Permissions
{
AI12-0444-1}
For the execution of a predefined operation of a signed integer type,
it is optional to raise Constraint_Error if the result is outside the
base range of the type, so long as the correct result is produced.
Discussion: Constraint_Error is never
raised for operations on modular types, except for divide-by-zero (and
rem/mod-by-zero).
An implementation
may provide additional predefined signed integer types[, declared in
the visible part of Standard], whose first subtypes have names of the
form Short_Integer, Long_Integer, Short_Short_Integer, Long_Long_Integer,
etc. Different predefined integer types are allowed to have the same
base range. However, the range of Integer should be no wider than that
of Long_Integer. Similarly, the range of Short_Integer (if provided)
should be no wider than Integer. Corresponding recommendations apply
to any other predefined integer types. An implementation may support
base ranges for which there is no corresponding named integer type. The
range of each first subtype should be the base range of its type.
{
AI12-0444-1}
Implementation defined: The predefined integer types declared
in Standard.
{
AI12-0444-1}
An implementation may provide
nonstandard integer
types, descendants of
root_integer that are declared outside
of the specification of package Standard, which may have different characteristics
than a type defined by an
integer_type_definition.
For example, a nonstandard integer type can have an asymmetric base range
or it can be disallowed as an array or loop index (a very long integer).
Any type descended from a nonstandard integer type is also nonstandard.
An implementation may place arbitrary restrictions on the use of such
types; it is implementation defined whether operators that are predefined
for “any integer type” are defined for a particular nonstandard
integer type. [In any case, such types are not permitted as
explicit_generic_actual_parameters
for formal scalar types — see
12.5.2.]
Implementation defined: Any nonstandard
integer types and the operators defined for them.
For a one's complement machine,
the high bound of the base range of a modular type whose modulus is one
less than a power of 2 may be equal to the modulus, rather than one less
than the modulus. It is implementation defined for which powers of 2,
if any, this permission is exercised.
{
8652/0003}
{
AI95-00095-01}
For a one's complement machine, implementations may support nonbinary
modulus values greater than System.Max_Nonbinary_Modulus. It is implementation
defined which specific values greater than System.Max_Nonbinary_Modulus,
if any, are supported.
Reason: On a one's complement machine,
the natural full word type would have a modulus of 2**Word_Size–1.
However, we would want to allow the all-ones bit pattern (which represents
negative zero as a number) in logical operations. These permissions are
intended to allow that and the natural modulus value without burdening
implementations with supporting expensive modulus values.
Implementation Advice
An implementation should support
Long_Integer in addition to Integer if the target machine supports 32-bit
(or longer) arithmetic. No other named integer subtypes are recommended
for package Standard. Instead, appropriate named integer subtypes should
be provided in the library package Interfaces (see
B.2).
Implementation Advice: Long_Integer should
be declared in Standard if the target supports 32-bit arithmetic. No
other named integer subtypes should be declared in Standard.
Implementation Note: To promote portability,
implementations should explicitly declare the integer (sub)types Integer
and Long_Integer in Standard, and leave other predefined integer types
anonymous. For implementations that already support Byte_Integer, etc.,
upward compatibility argues for keeping such declarations in Standard
during the transition period, but perhaps generating a warning on use.
A separate package Interfaces in the predefined environment is available
for pre-declaring types such as Integer_8, Integer_16, etc. See
B.2.
In any case, if the user declares a subtype (first or not) whose range
fits in, for example, a byte, the implementation can store variables
of the subtype in a single byte, even if the base range of the type is
wider.
An implementation for a two's
complement machine should support modular types with a binary modulus
up to System.Max_Int*2+2. An implementation should support a nonbinary
modulus up to Integer'Last.
Implementation Advice: For a two's complement
target, modular types with a binary modulus up to System.Max_Int*2+2
should be supported. A nonbinary modulus up to Integer'Last should be
supported.
Reason: Modular types provide bit-wise
"and", "or", "xor",
and "not" operations. It is important for systems programming
that these be available for all integer types of the target hardware.
Ramification: Note that on a one's complement
machine, the largest supported modular type would normally have a nonbinary
modulus. On a two's complement machine, the largest supported modular
type would normally have a binary modulus.
Implementation Note: Supporting a nonbinary
modulus greater than Integer'Last can impose an undesirable implementation
burden on some machines.
NOTE 1
Integer
literals are of the anonymous predefined integer type
universal_integer.
Other integer types have no literals. However, the overload resolution
rules (see
8.6, “
The
Context of Overload Resolution”) allow expressions of the type
universal_integer whenever an integer type is expected.
NOTE 2 The same arithmetic operators
are predefined for all signed integer types defined by a
signed_integer_type_definition
(see
4.5, “
Operators
and Expression Evaluation”). For modular types, these same
operators are predefined, plus bit-wise logical operators (
and,
or,
xor, and
not). In addition, for the unsigned
types declared in the language-defined package Interfaces (see
B.2),
functions are defined that provide bit-wise shifting and rotating.
Examples
Examples of integer
types and subtypes:
type Page_Num is range 1 .. 2_000;
type Line_Size is range 1 .. Max_Line_Size;
subtype Small_Int is Integer range -10 .. 10;
subtype Column_Ptr is Line_Size range 1 .. 10;
subtype Buffer_Size is Integer range 0 .. Max;
type Byte is mod 256; -- an unsigned byte
type Hash_Index is mod 97; -- modulus is prime
Extensions to Ada 83
An implementation is allowed
to support any number of distinct base ranges for integer types, even
if fewer integer types are explicitly declared in Standard.
Modular (unsigned, wrap-around) types are new.
Wording Changes from Ada 83
Ada 83's integer types are now called "signed"
integer types, to contrast them with "modular" integer types.
Standard.Integer, Standard.Long_Integer, etc.,
denote constrained subtypes of predefined integer types, consistent with
the Ada 95 model that only subtypes have names.
We now impose minimum requirements on the base
range of Integer and Long_Integer.
We no longer explain integer type definition
in terms of an equivalence to a normal type derivation, except to say
that all integer types are by definition implicitly derived from root_integer.
This is for various reasons.
First of all, the equivalence with a type derivation
and a subtype declaration was not perfect, and was the source of various
AIs (for example, is the conversion of the bounds static? Is a numeric
type a derived type with respect to other rules of the language?)
Secondly, we don't want to require that every
integer size supported shall have a corresponding named type in Standard.
Adding named types to Standard creates nonportabilities.
Thirdly, we don't want the set of types that
match a formal derived type "type T is new Integer;" to depend
on the particular underlying integer representation chosen to implement
a given user-defined integer type. Hence, we would have needed anonymous
integer types as parent types for the implicit derivation anyway. We
have simply chosen to identify only one anonymous integer type —
root_integer, and stated that every integer type is derived from
it.
Finally, the “fiction” that there
were distinct preexisting predefined types for every supported representation
breaks down for fixed point with arbitrary smalls, and was never exploited
for enumeration types, array types, etc. Hence, there seems little benefit
to pushing an explicit equivalence between integer type definition and
normal type derivation.
Extensions to Ada 95
{
AI95-00340-01}
The Mod attribute is new. It eases mixing of signed
and unsigned values in an expression, which can be difficult as there
may be no type which can contain all of the values of both of the types
involved.
Wording Changes from Ada 95
{
8652/0003}
{
AI95-00095-01}
Corrigendum: Added additional permissions for modular types on
one's complement machines.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe