#### Fixed Point, Modular, and Universal Types

The only fixed point type defined in package Standard is Duration. However, Ada lets us define our own fixed point types. We specify the accuracy with the reserved word delta, and a range constraint is required. For example,

type Voltage is delta 0.01 range -20.0 .. 20.0;

This guarantees that the objects of type Voltage will be represented with at least an accuracy of 1/100. Since the computer is binary, Ada will choose an internal representation at least as accurate as 1/128. It might use even greater accuracy, for example, 1/256. In any event, it's guaranteed that the accuracy is at least as good as that requested.

It's possible to make a request that a particular implementation of Ada can't handle. For example, if we write

type Voltage is delta 1.0E-10 range 0.0 .. 1.0E9;the Ada compiler that we're using may have to report that it has no internal representation that satisfies this requirement. (Almost any

**delta**would be unreasonable if Ada didn't require range constraints on all fixed point types.)

The set of numbers that can be represented exactly by any implementation of Ada
that accepts a type definition like the above is called the **model numbers** of
that type. This applies to floating types as well as fixed, for example,

type W is digits 5 range 0.0 .. 100.0;

A particular implementation may represent additional numbers exactly; these are
called **safe numbers**. The safe numbers are a superset of the model numbers;
their range usually is a little larger.

We can add and subtract objects of a fixed point type. However, if we multiply or divide them in Ada 83, we must immediately convert the result to the same or another numeric type before we can store it. For example,

V1, V2, V3 : Voltage; ... V1 := V2+V3; -- legal V1 := V2*V3; -- illegal in Ada 83, legal in Ada 95 V1 := Voltage(V2*V3); -- legal

In Ada 95, we can multiply two numbers of a fixed point type without an explicit type conversion if the type of the multiply is uniquely determined. In the previous example, the multiplication is legal in Ada 95 because storing the result into V1 uniquely determines the type of the product of V2 and V3.

Note that **V1 := V1*V2*V3** is illegal even in Ada 95, because the intermediate
result has no uniquely determined type.

Also note that if we have **procedure Display(V : in Voltage);** then in Ada 95 we
can write

Display(V2*V3);because the type of V2*V3 is uniquely determined by the procedure call.

Ada.Text_IO contains a generic package Fixed_IO for I/O of fixed point types.

Ada 95 also provides **modular types**. These are unsigned integers, and the
arithmetic is performed modulo a specified number so that overflow can never
occur. For example, in Ada 95 we can write:

type Unsigned_Byte is mod 256;

The modulus doesn't have to be a power of two, but it often is. If we
now declare **A : Unsigned_Byte := 100;** and **B : Unsigned_Byte
:= 200;** then the result of **A + B** will be 300 mod 256, or 44.

In Ada 95, the package **Interfaces** is supplied, and it defines a modular type
for each signed integer type. For example, an implementation of Ada 95 might
define the types **Unsigned_8**, **Unsigned_16**, and **Unsigned_32**. Type **Unsigned_8**
corresponds to the example **Unsigned_Byte** above.

When we declare a variable in Ada, we give its type. But when we declare a constant, we may or may not give its type. For example,

L : constant Integer := 30; M : constant := 1000; E : constant Float := 2.718281828; Pi : constant := 3.141592654;

Also, when we write a number, such as 3.0 or 29_999, we usually don't qualify
it with a type (for example, we usually don't write **Float'(3.0)**).

Suppose an implementation of Ada provides types Integer, Long_Integer, Float,
and Long_Float. How can Ada determine the types of M, Pi, 3.0, and 29_999? M
and 29_999 are said to be of type **universal_integer**; they can assume any
integer type as required. Pi and 3.0 are said to be of type **universal_real** and
can assume any floating or fixed point type as required.

We can't explicitly declare objects to be of universal types. However, we can write

M : constant := 1000; Pi : constant := 3.141592654; I : Integer; LI : Long_Integer; F : Float; LF : Long_Float; ... I := M; LI := M; I := 29_999; LI := 29_999; F := Pi; LF := Pi; F := 3.0; LF := 3.0;and in each case the constant assumes the correct type. The result of multiplying or dividing two numbers of a fixed point type is said to be of type

**universal_fixed**. As we pointed out, this result must be converted to some numeric type before it can be stored.

Most of the attributes that produce integer results, like **Pos**, are of type
universal_integer. For example, with the declarations above, we could write

I := Character'Pos('A'); LI := Character'Pos('A');

#### Question

Which one of the following declarations is**illegal**?