Contents   Index   Search   Previous   Next

3.5.4 Integer Types

   {integer type} {signed integer type} {modular type} 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. {unsigned type: See modular type}


integer_type_definition ::= signed_integer_type_definition | modular_type_definition
signed_integer_type_definition ::= range static_simple_expression .. static_simple_expression
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.
modular_type_definition ::= mod static_expression

Name Resolution Rules

   {expected type (signed_integer_type_definition simple_expression) [partial]} Each simple_expression in a signed_integer_type_definition is expected to be of any integer type; they need not be of the same type. {expected type (modular_type_definition expression) [partial]} The expression in a modular_type_definition is likewise expected to be of any integer type.

Legality Rules

   The simple_expressions of a signed_integer_type_definition shall be static, and their values shall be in the range System.Min_Int .. System.Max_Int.
   {modulus (of a modular type)} {Max_Binary_Modulus} {Max_Nonbinary_Modulus} 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.
   {base range (of a signed integer type) [partial]} A signed_integer_type_definition defines an integer type whose base range includes at least the values of the simple_expressions and is symmetric about zero, excepting possibly an extra negative value. {constrained (subtype)} {unconstrained (subtype)} A signed_integer_type_definition also defines a constrained first subtype of the type, with a range whose bounds are given by the values of the simple_expressions, converted to the type being defined.
Implementation Note: The base range of a signed integer type might be much larger than is necessary to satisfy the aboved 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). {subtype conversion (bounds of signed integer type) [partial]}
    {base range (of a modular type) [partial]} A modular_type_definition defines a modular type whose base range is from zero to one less than the given modulus. {constrained (subtype)} {unconstrained (subtype)} 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.
    {Integer} 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.
    {Natural} {Positive} 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;
    {root_integer} {Min_Int} {Max_Int} 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. {universal_integer [partial]} {integer literals} [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.
{8652/0099} Note that this derivation does not imply any inheritance of subprograms. Subprograms are inherited only for types derived by a derived_type_definition (see 3.4), or a private_extension_declaration (see 7.3, 7.3.1, and 12.5.1).
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.
    {position number (of an integer value) [partial]} The position number of an integer value is equal to the value.
    For every modular subtype S, the following attribute is defined:
S'Modulus yields the modulus of the type of S, as a value of the type universal_integer.

Dynamic Semantics

    {elaboration (integer_type_definition) [partial]} The elaboration of an integer_type_definition creates the integer type and its first subtype.
    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.
    {Overflow_Check [partial]} {check, language-defined (Overflow_Check)} {Constraint_Error (raised by failure of run-time check)} 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. [{Division_Check [partial]} {check, language-defined (Division_Check)} {Constraint_Error (raised by failure of run-time check)} For any integer type, Constraint_Error is raised by the operators "/", "rem", and "mod" if the right operand is zero.]

Implementation Requirements

    {Integer} In an implementation, the range of Integer shall include the range -2**15+1 .. +2**15-1.
    {Long_Integer} 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

    For the execution of a predefined operation of a signed integer type, the implementation need not 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).
    {Long_Integer} {Short_Integer} 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. There need not be a named integer type corresponding to each distinct base range supported by an implementation. The range of each first subtype should be the base range of its type.
Implementation defined: The predefined integer types declared in Standard.
    {nonstandard integer type} An implementation may provide nonstandard integer types, descendants of root_integer that are declared outside of the specification of package Standard, which need not have all the standard characteristics of a type defined by an integer_type_definition. For example, a nonstandard integer type might have an asymmetric base range or it might not be allowed 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.
    {one's complement (modular types) [partial]} 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} For a one's complement machine, implementations may support non-binary 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

    {Long_Integer} 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 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.
    {two's complement (modular types) [partial]} 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.
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.
25  {universal_integer} {integer literals} 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.
26  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.
27  Modular types match a generic_formal_parameter_declaration of the form "type T is mod <>;"; signed integer types match "type T is range <>;" (see 12.5.2).


    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

{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.

Contents   Index   Search   Previous   Next   Legal