Gem #86: Ada Quiz 1 - Basic Types

by Quentin Ochem —AdaCore

Let's get started...

Here are ten short questions involving Ada scalar types and expressions. Try to answer them without using the compiler. Check how good an Ada expert you are!

Q1 - Is there a compilation error?

   V : Float := 10;

Q2 - What's the output of this code?

   type Float_1 is digits 5;
   type Float_2 is digits 7;
   V1 : Float_1 := 10.0E10;
   W1 : Float_1 := V1 + 1.0;
   V2 : Float_2 := 10.0E10;
   W2 : Float_2 := V2 + 1.0;
begin
   Put_Line (Boolean'Image (V1 = W1));
   Put_Line (Boolean'Image (V2 = W2));
end;

Q3 - Is there a compilation or run-time error?

   type Digit is mod 10;
   V1 : Digit := 10;
   V2 : Digit := 9 + 1;

Q4 - What's the output of this code?

   F   : Float   := 7.6;
   Div : Integer := 10;
begin
   F := Float (Integer (F) / Div);
   Put_Line (Float’Image (F));
end;

Q5 - Is there a run-time error?

   type Small_Int is range 1 .. 10;
   V : Small_Int := 9;
   W : Small_Int := 2;
begin
   V := V + W - 1;
end;

Q6 - Is there a run-time error?

   type Small_Int is range 1 .. 10;
   V : Small_Int := 9;
   W : Small_Int := 2;
begin
   V := Small_Int (V + W) - 1;
end;

Q7 - Is there a compilation or run-time error?

   C1 : constant := 2 ** 1024;
   C2 : constant := 2 ** 1024 + 10;
   C3 : constant := C1 - C2;
   V  : Integer  := C1 - C2;

Q8 - Is there a compilation error?

   type T is (A, B, C);
   V1 : T := T'Val ("A");
   V2 : T := T'Value (2);

Q9 - Is there a run-time error?

   type T is (A, B, C);
   V1 : T := T'Value ("A");
   V2 : T := T'Value ("a");
   V3 : T := T'Value (" a ");

Q10 - Is there a compilation error?

   type T is range 1 .. 0;
   V : T;

Answers:

Q1 - Compilation error

V is declared with type Float, and it must be initialized with a real value. The literal 10 has an integer value, so the Ada compiler rejects it. Replacing it with 10.0 fixes the error.

Q2 - Outputs TRUE and FALSE (implementation-dependent)

Types Float_1 and Float_2 have been specified with a minimum number of decimal digits, but the compiler is free to choose representations with greater precision. In this example, GNAT selects a 32-bit type with 6 digits of precision for Float_1 and a 64-bit type with 15 digits of precision for Float_2. For Float_1, the operation V1 := V1 + 1.0 doesn't change the value of V1 because, given the precision, the increment is too small compared to the initial value. For Float_2, the representation has sufficient additional precision that the increment results in a distinct representation value.

Q3 - Compilation error

Type Digit has a modulus of 10, so its values range from 0 to 9. The value 10 is outside that range, so "X := 10" is rejected by the compiler. Interestingly though, "X := 9 + 1" is legal. The "+" used here is the type's modular addition operator, which will wrap around, giving a result of 0.

Q4 - 0.0

The argument of the conversion to Float, Integer (F) / Div, converts the value of F (7.6) to type Integer, which is rounded to 8. Then 8 is divided by 10, using Integer division, resulting in 0. Converting this back to Float gives 0.0. This is a common sort of error, where the conversion is not done on the appropriate variables. Assuming that floating-point division is desired, the expression should be written as F / Float (Div), which produces the desired result of 0.76.

Q5 - No run-time error

Although V + W is equal to 11, which exceeds the maximum value of Small_Int, predefined arithmetic operators operate on values of the underlying type. In this case, the compiler will most likely choose a type with a much larger range than that of the first subtype Small_Int. Thus, the expression will be computed correctly as 10, which satisfies the constraint of the target variable.

Q6 - Run-time error

In this case, V + W is computed and converted to Small_Int. Although the expression is already of the right type (Small_Int'Base), the explicit conversion checks that the value is in the range of the target subtype (Small_Int'Range). Here the sum is outside the range of Small_Int, so an exception is raised at run time. Note that a more appropriate way to write such an expression (when it makes sense) is to use a qualified expression (Small_Int'(V + W)) instead of a conversion, since the operand is already of the right type.

Q7 - No errors

The value 2 ** 1024 is far larger than the integer values that can be represented at run time. Specifically, it exceeds the upper bound of the largest integer type supported by GNAT (64-bit integer). But C1 is a named number, not a typed constant. The compiler uses an internal representation with unbounded precision to compute the values of named numbers and static expressions at compile time without overflowing. The potentially large intermediate representation does not get stored within the program's object code. In this particular example, for the assignment to V, the subtraction is computed by the compiler, and V is assigned the value -10.

Q8 - Compilation errors

T'Val returns the enumeration value corresponding to an argument of an integer type. On the other hand, T'Value takes a string argument and returns the type's corresponding value (if any). In this example, the arguments are of the wrong type.

Q9 - No run-time error

Conversions from string representation to scalar are case-insensitive, and surrounding spaces are ignored.

Q10 - No compilation error

The type T has an empty range, so there are no valid representable values. Although this is an odd sort of type to define, it's perfectly legal. Of course any attempt to assign a specific integer value to the variable will raise an exception.


About the Author

Quentin Ochem has a software engineering background, specialized in software development for critical applications. He has over 10 years of experience in Ada development. He works today as a technical account manager for AdaCore, following projects related to avionics, railroad, space and defense industries. He also teaches the avionics standard DO-178B course at the EPITA University in Paris.