

                                                        Chapter 6
                                          ADDITIONAL SCALAR TYPES


OUR OWN TYPES
_________________________________________________________________

Most of the example programs in this tutorial have used the
predefined type INTEGER for illustrating various concepts, and it
is an excellent choice due to its versatility.  There are other
types available because they are part of the Ada definition, and
we can define our own types for special purposes.  This chapter
will illustrate to you some of the reasons for doing so.

A complete description of types will be given in the next chapter
but first we will learn how to use some of them.  We are caught in
a dilemma like the proverbial, "which came first, the chicken or
the egg?", and must select which to define first.  We have chosen
to illustrate usage in this chapter, and then give the details of
type definition in the next chapter.



A FEW integer CLASS TYPES
_________________________________________________________________

Examine the file named ALLINT.ADA for some       ================
examples of how and why we can use our own type     ALLINT.ADA
definitions.  The three types illustrated in     ================
lines 10 through 12 are available with all Ada
compilers because they are so versatile and
useful, and because they are required by the Ada programming
language.  As we have said before, the type INTEGER defines a
variable that can have any whole value between -32768 to 32767 on
most microcomputers and some minicomputers too.  It should be
pointed out that most minicomputers use a much larger range as
standard.  The type NATURAL defines a variable from 0 to 32767, and
the type POSITIVE covers the range from 1 to 32767 on most
microcomputers.

Consider the word "most" in the last paragraph, and think about the
problems you could have if you wrote a program that depended on a
particular variable covering the listed range, and tried to move
the program to a different machine which used a different range.
You could be faced with a large rewrite problem in order to get
the program to work on the new computer.



HOW CAN WE HELP SOLVE PORTABILITY PROBLEMS
_________________________________________________________________

Suppose we defined our type to cover a certain range, such as
illustrated in line 14 of the example program, and moved the

                                                         Page 6-1

                              Chapter 6 - Additional Scalar Types

program to another computer.  According to the definition of Ada,
the new compiler would be obligated to create a type for us that
would cover the given range, or give us a compile time error
telling us that the hardware simply could not support the defined
range.  In this case, due to the rather small range requested, any
meaningful compiler and machine combination would be able to cover
the defined range, and we would have a program that would run in
spite of differences in the way the standard types were defined.
Good programming practice, especially if the source code may need
to be moved to other computers, would define all ranges explicitly
and avoid the implementation defined limits built into the
compiler.

Two new types are defined in lines 14 and 15, and the program uses
some new attributes to illustrate the new types.  In lines 24 and
27, we use two attributes which we have used before, but in lines
31 and 34, we use two new attributes.  In order for the system to
create a type which covers a range of -1000 to 24000, it must use
a structure with enough binary bits to cover the given range.  The
range is not composed of binary limits so the system will have to
define enough bits to cover this range and a little more.  It will
probably define some number of 8-bit bytes and the range covered
by the full pattern, as defined, is called the base range.  The two
new attributes give the limits of the base selected by the system.
The base limits will probably be -32768 to 32767 if you are using
a microcomputer, as you will see when you execute the program.



DO YOU HAVE A SMART COMPILER?
_________________________________________________________________

The type illustrated here named MY_SHORT has defined limits of -12
and 127, a relatively small range.  It is small enough that it can
fit into a base range of -128 to 127, which could be stored in a
single 8-bit byte.  If your compiler is smart enough to realize
that, it could use a single 8-bit byte to store every variable of
this type, and if you had a lot of these to store, it would save
you a lot of memory.  You will probably find however, that most
compilers will simply use the full INTEGER range for the base type
of even this small number.

Four attributes of two different types are displayed on the monitor
for your information.  You can see from the results of running this
program exactly how your compiler stores these two types.



THE in AND not in OPERATORS
_________________________________________________________________

We have a new operator to learn about now, the in operator
illustrated in line 48.  If the variable Index, which has a current
value of 345 due to initialization, is within the defined range of

                                                         Page 6-2

                              Chapter 6 - Additional Scalar Types

the subtype MY_SUBTYPE, a BOOLEAN type TRUE will be returned,
otherwise a BOOLEAN type FALSE is returned.  This result can be
assigned to a BOOLEAN variable or used for a boolean decision as
shown.  In this case, the value of Index is not in the range of
MY_SUBTYPE so a FALSE is returned and the message will not be
output.  Another operation is illustrated in line 53 which is the
not in operation, and should be self explanatory.  You should be
able to see that the message in line 53 will be displayed.  The in
and not in operators are further illustrated in lines 56 and 60
where an explicit range is used for the test range.

Be sure to compile and run this program and observe the output.
Here is a chance for you to see if you have a smart compiler.



THE ENUMERATED TYPE
_________________________________________________________________

Examine the program named ENUM.ADA for our first ================
look at an enumerated type and how it is used in     ENUM.ADA
a program.  Line 7 is the first definition of an ================
enumerated type, and uses the reserved words
type and is as shown.  The type name is given
between the two reserved words and the values which a variable of
this type is allowed to have assigned to it are given as a list
within parentheses.  The values actually represent numerical values
from 0 up to that value required for the largest value, in this
case 6, since the numbering will be assigned from 0 to 6.  In line
20, the variable named Day_Of_Week is declared to be of type DAY,
so it can be assigned any of the 7 values listed for the type DAY,
and no others.  We could assign the values 0, 1, 2,.. 6 to
represent the 7 days of the week and use the numerical values
within the program, but by using the enumerated type, we can refer
to Sunday as SUN, Monday as MON, etc., making the program much
clearer and easy to follow.

Enumerated types are always locally defined because there are no
predefined enumerated types in the Ada language.  There is one
exception to this, because the BOOLEAN type is an enumerated type
with two possible values, but it has some special properties
available with no other enumerated variables.  These will be
discussed using the next example program.

Jumping ahead to the executable code in the current example
program, we illustrate assignment in line 28, where we assign the
value of WED to the variable Day_Of_Week.  Lines 29 and 30
illustrate the FIRST and LAST attributes which we have seen before
for integer type variables.  Just as -32768 is the lowest possible
value that can be assigned to an INTEGER type variable, MON is the
lowest, and hence the first, value that can be assigned to a
variable of type DAY.



                                                         Page 6-3

                              Chapter 6 - Additional Scalar Types

TWO NEW ATTRIBUTES
_________________________________________________________________

Lines 31 and 32 illustrate the attributes PRED, which means the
predecessor, and SUCC, which means the successor.  PRED returns the
value of the predecessor of the present value of the variable used
as an argument.  Since the variable Day_Of_Week was assigned the
value of SUN in line 30, and the day just prior to SUN is SAT, SAT
is assigned to the variable Day_Of_Week in line 31.  It is an error
to attempt to take the PRED of a variable which contains the first
value in the available list, and will result in raising the
exception Range_Error.  Likewise, an attempt to take the SUCC of
any variable that is at its maximum value will result in the
exception Range_Error being raised.  Exceptions will be covered in
detail later in this tutorial.  At this time simply remember that
an exception refers to an exceptional condition or an error.



WHAT IS A SUBTYPE OF AN ENUMERATED TYPE?
_________________________________________________________________

In lines 8 and 9, we define two subtypes of the type DAY which will
have all the characteristics of type DAY except for a more
restricted range.  A variable that is declared to be of type
PLAY_DAY can be assigned either of two values, SAT or SUN.  SAT
will have a numerical value of 5, and SUN will have a numerical
value of 6, both of these being inherited from the parent type,
DAY.  Thus in line 32, we use the attribute FIRST to get the first
day of type PLAY_DAY, which will be SAT, then use the attribute
SUCC to get the successor of that value, which will be SUN.  Notice
how the attributes can be combined to obtain the needed
information.  A subtype is assignment compatible with its parent
type.  We will discuss subtypes in greater detail in the next
chapter of this tutorial.



NOW FOR THE POS AND VAL ATTRIBUTES
_________________________________________________________________

The POS attribute will return a value of type universal_integer,
the value representing the position of the enumerated value within
the parentheses as shown in lines 33 and 34.  The VAL attribute
will return the enumerated value of the numerical value included
in the parentheses.  Notice that if the type DAY in line 35 were
changed to PLAY_DAY, an error would be returned, since that is an
illegal enumerated value for that type.  The error would be
returned by raising the exception Range_Error.






                                                         Page 6-4

                              Chapter 6 - Additional Scalar Types

WHAT ABOUT ENUMERATED ASSIGNMENTS?
_________________________________________________________________

The value of Happy_Day can be assigned to Day_Of_Week at any time
because they are both of the same type, and any value that can be
legally assigned to Happy_Day can also be assigned to Day_Of_Week.
Day_Of_Week cannot always be assigned to Happy_Day however, because
Day_Of_Week is permitted to contain some values which are not legal
to assign to Happy_Day.  This would illustrate that some care must
be exercised when using the enumerated type, but if used properly,
it can help in program debugging by the use of the strong
type-checking defined into the Ada language.



USING ENUMERATED TYPES FOR CONTROL
_________________________________________________________________

The loop in lines 37 through 40 covers exactly the range covered
by the subtype WORK_DAY, so we can use it in the range part of the
definition of the loop.  When you run this program, you will see
that the loop will be executed exactly five times.

Lines 42 through 51 contain two relational checks on the variable
Today to illustrate that the enumerated type variable can be used
in a BOOLEAN expression.  All of the boolean operators are
available, which includes the following list, and no others;

     =     equality
     /=    inequality
     >     greater than
     >=    greater than or equal to
     <     less than
     <=    less than or equal to

No mathematical operations are available with enumerated type
variables.  Assignments are available as illustrated in the present
example program.



WHAT IS QUALIFICATION?
_________________________________________________________________

In lines 53 and 54, we assign the same value to two different
enumerated type variables.  At least it seems to be the same value.
In actuality, they are two different values with the same name,
namely SUN.  Because Ada does such strong type checking, it is
smart enough to realize that they are actually two different
constants and it will select the one that it needs for each
statement based on the type of the variable to which it will be
assigned.



                                                         Page 6-5

                              Chapter 6 - Additional Scalar Types

Lines 56 and 57 make the identical assignments by qualifying which
value you are interested in, but in this case, the qualifications
are unnecessary.  There could be a case when you would need to tell
the system which value of SUN you are interested in.  Qualification
uses the type followed by a "tick", or apostrophe, prior to the
enumeration value.



OUTPUTTING ENUMERATED VARIABLES
_________________________________________________________________

The statements in lines 59 and 60 output the current value of the
variable Today, and the predecessor of the current value.  Finally,
the same values are output for the variable Big_Sphere, and when
you run the program, you will see that the same value is output for
the first value in each line, but the second values differ for the
two variables.  Note the four extra lines given in program lines
14 through 18.  These are used to tell the system how to output
enumerated variables, and we will cover the essentials of how this
works very soon.

The in and not in operators which we studied in the last program
are available for use with the enumerated type variable.  In fact,
they are available with all discrete types.  Be sure to compile and
run this program and after studying the results, see if you can
modify the program to output additional enumerated values.



THE BOOLEAN VARIABLE
_________________________________________________________________

The program named BOOLVARS.ADA is an             ================
illustration of how to use the BOOLEAN variable,   BOOLVARS.ADA
which is actually a special case of an           ================
enumerated variable.  Every enumerated type must
be defined by the user, before use, with the
exception of the predefined BOOLEAN type.  It is simply an
enumerated type with two possible values, TRUE or FALSE.  Since it
is an enumerated type, all of the operations available with the
enumerated type are available with the BOOLEAN type, including all
six of the relational operators, the assignment operator, the
attributes, and no mathematical operators.



THE LOGICAL OPERATORS
_________________________________________________________________

The BOOLEAN type has some of its own unique operators that are
available with no other types, the logical operators.  The logical
operators were defined earlier, but are repeated here as a complete
list.

                                                         Page 6-6

                              Chapter 6 - Additional Scalar Types

     and         logical and operation
     or          logical or operation
     xor         exclusive or of two values
     not         inversion of the value
     and then    short circuit and operation
     or else     short circuit or operation

It should be pointed out that FALSE is of less numerical value than
TRUE, by definition, and in actuality, the value of FALSE is 0, and
the value of TRUE is 1.
The program illustrates how to output BOOLEAN values in lines 29
and 30, after the package instantiation in lines 7 and 8.  Notice
that the Enumeration_IO library is used for BOOLEAN output
illustrating again that BOOLEAN is a special case of the enumerated
type.

Be sure to compile and execute this program to see that it really
does compile as stated.  Someday, you will need the ability to
display the BOOLEAN results as is done in this program.



SOME USELESS ATTRIBUTES OF INTEGER TYPES
_________________________________________________________________

It may seem silly to illustrate some useless      ===============
attributes but the program named INCRINT.ADA        INCRINT.ADA
does that very thing.  The POS attribute of an    ===============
integer variable is defined as being the number
itself, and the VAL is also the number.  The
SUCC of an integer variable is the next number, and the PRED is the
predecessor.  These last two attributes could be useful for
incrementing or decrementing a variable in a program, but good
programming practice would forbid such a use of these attributes.
You should use the very clear and easy to understand method of
adding one to the value and assigning the result back to the
variable, as illustrated in line 17 of the program.

Even though these are really useless at this point in time, the
fact that this can be done will be very useful when we get to the
study of generics later in this tutorial.

Compile and run this program, adding some output to gain some of
your own programming experience.


FLOATING POINT VARIABLES
_________________________________________________________________

Examine the program named FLOATVAR.ADA for       ================
examples of nearly all operations possible with    FLOATVAR.ADA
floating point numbers.                          ================



                                                         Page 6-7

                              Chapter 6 - Additional Scalar Types

We begin, in lines 7 and 8, by defining two constants, the second
being defined in terms of the first.  Remember that any thing used
in an Ada program must be previously defined, and you will know all
of the rules for defining a value in terms of some other value.
The two constants are of type universal_real, so they can be used
with any of the various real types we will encounter in this
program.  We declare a variable named R in line 10 of type FLOAT,
which is defined by the compiler writer, then two new types of
floating point numbers, and we finally declare six variables of
various types.

Two additional floating point types, SHORT_FLOAT and LONG_FLOAT are
defined as optional by the LRM and may be available with your
compiler.  You can find out by checking the documentation supplied
with your compiler or by declaring variables of those types to see
if the compiler will accept the declarations.  If they do exist,
you can determine their limits by using attributes as defined
below.


HOW TO DECLARE A NEW FLOATING POINT TYPE
_________________________________________________________________

The reserved word digits used in line 12 tells the compiler that
you don't care how many bytes of storage it uses to define the
number, but it must store at least 7 significant digits for every
variable of type MY_FLOAT.  Line 13 requests a minimum of 15
significant digits for every variable of type MY_LONG_FLOAT.  Line
10, on the other hand, only requires that it be a floating point
number, and the compiler writer has the option of using as many
significant digits as he desires to implement variables of this
type.  If you wrote a program that ran well with one compiler, it
may not run properly with a different compiler, either because the
new one did not use enough significant digits, or because the new
one used far more causing your program to run out of storage space
or run too slowly.  The forms in lines 12 and 13 are therefore
preferred for portability purposes.  More will be said about
declaring floating point types in the next chapter of this
tutorial.


FLOATING POINT LITERALS
_________________________________________________________________

The distinguishing characteristic that defines a floating point
number is the use of a decimal point.  Ada requires at least one
digit before and after the decimal point, although either or both
may be zeros.  Single embedded underlines are allowed to improve
readability, but cannot be adjacent to the decimal point.  The
underlines are ignored by the compiler, and have no significance.
Any radix from 2 through 16 may be used by first giving the radix,
then enclosing the number in pound (#) characters.  The base 10 is
the default and need not be specified.  Exponential notation can
be used, the exponent being to the same base as that indicated by

                                                         Page 6-8

                              Chapter 6 - Additional Scalar Types

the radix.  A binary floating point literal is illustrated in line
31 of the program and you can see that the radix is similar to that
used for integer class literals.


FLOATING POINT MATHEMATICAL OPERATORS
_________________________________________________________________

Lines 29 through 35 illustrate the mathematical operators available
with floating point variables and should be self explanatory with
the exception of the exponential operator.  This can use only an
integer type of exponent but it can be either positive or negative.
Of course, zero is also permissible.

All six logical comparisons are available with floating point
variables as illustrated in lines 38 through 43.  The next two
lines, 45 and 46, illustrate some multiple mathematical operations.

As with all variables, the types must agree within all mathematical
and logical operations and the result must be assigned to the right
type of variable, or a type error will be generated at compile
time.  In line 46, the variable Area must be transformed in type
prior to being assigned to Cover since they are of different types.
The entire statement will be evaluated as of type MY_LONG_FLOAT,
since that will be the final result.  The constant 27.3, and the
constant PI, will be transformed automatically from universal_real
to MY_LONG_FLOAT prior to the multiplications.



NOW TO OUTPUT SOME FLOATING POINT VALUES
_________________________________________________________________

In lines 24 and 25 we instantiate a copy of the Float_IO package
for use with the type MY_FLOAT and use it in lines 49 through 55.
The variable Area will be output in a default exponential notation
in line 49, but with 5 digits prior to the decimal point in line
50.  Line 54 adds an additional 5, which will cause 5 digits to be
output following the decimal point, and the fourth field, in this
case a zero, causes the output to be written with a zero exponent,
or no exponential notation.



FLOATING POINT ATTRIBUTES
_________________________________________________________________

Floating point variables and types are no different from the scalar
types concerning attributes available for your use, except that
there are different attributes available.  Lines 58 through 72
illustrate the use of some of the available attributes.  The
attribute named DIGITS, gives the number of significant digits
available with the specific type, and the return is a
universal_integer type.

                                                         Page 6-9

                              Chapter 6 - Additional Scalar Types


The attributes named SMALL and LARGE give the smallest and largest
numbers available with the corresponding type, and the attributes
named FIRST and LAST combined with the BASE attribute as shown in
lines 68 and 71, define the extreme values as used by the
underlying base type of the actual user's type.  All four of these
attributes return a value of the type universal_real, and are
displayed on the monitor for your information.  Note that there are
other attributes available with the floating point type, but only
these will be elaborated upon at this time.

See appendix A of the LRM for additional information on attributes.
This appendix lists all of the attributes available with an Ada
system.

Compile and run this program and observe the output.  The actual
output is the clearest description of the Put procedure when used
with floating point numbers.  Study the result of the attribute
outputs before continuing on to the next example program.



FIXED POINT VARIABLES
_________________________________________________________________

Fixed point variables are a relatively new        ===============
concept and may be a bit confusing, but the file     FIXED.ADA
named FIXED.ADA will illustrate the use of a few  ===============
fixed point variables.  Line 9 defines a fixed
point type as having a range of -40.0 to 120.0,
and a delta of 0.1 which means that a variable of this number can
only have a value that is accurate to one digit after the decimal
point.  There are therefore a fixed number of digits before and
after the decimal point, hence the name of this type of variable.


A fixed point number will always be exact since it is defined that
way.  There can never be a gradual accumulation of error with a
fixed point variable.  In order to completely understand the fixed
point type, one would require a complete understanding of numerical
analysis, which is beyond the scope of this tutorial.  The program
before you will illustrate how to use this type, but no attempt
will be made to explain why it should be used.

There are no predefined fixed point types, so it is up to the
programmer to define every fixed point type needed, as illustrated
in lines 9 and 10.  The reserved word delta denotes a fixed point
type and a range is required for every fixed point type.  Lines 12
and 13 are used to declare a few variables for use in the program,
then lines 17 through 20 instantiate the package Fixed_IO for use
with our two fixed point types.




                                                        Page 6-10

                              Chapter 6 - Additional Scalar Types

HOW DO WE USE FIXED POINT TYPES?
_________________________________________________________________

Output of a fixed point type uses the same format as that defined
for floating point data as shown in line 27.  When we come to
arithmetical operations, we find some funny rules which we will
simply state, and make no attempt to justify.  Variables of the
same fixed point types can be added and subtracted freely, provided
the results are within the defined range, just like floating point
type variables.  Multiplication by a constant of type
universal_integer is permitted, resulting in the same fixed point
type we started with.  Multiplication of two fixed point variables
results in an anonymous type which must be explicitly converted to
some predefined type, as illustrated in lines 33 and 34.  The only
operator available with the fixed types is the abs operator.

Many attributes are available with the fixed point type, some of
which are illustrated in lines 46 through 61.  The attributes named
DELTA, SMALL, and LARGE, each return a value which is of type
universal_real, and must be converted to the users fixed type, by
a type conversion, before the result can be used in the program.
Line 48 illustrates the conversion within the Put procedure call.
Lines 51 and 54 illustrates the explicit conversion to a FLOAT
type, but since the results are to be used as FLOAT variables, and
the universal_real type can be used directly as a FLOAT type, the
conversion is not actually required.

Be sure to compile and run this program and observe the output to
see if it conforms to what you think it should do based on the
previous discussion.  Note that your compiler may not generate
identical output as that listed in the result of execution due to
different compiler defaults.



MIXING VARIOUS TYPES
_________________________________________________________________

Examine the program named MIXTYPES.ADA for       ================
examples of using various types together.  It is   MIXTYPES.ADA
meant to be an illustration of how to combine    ================
some of the various types available in Ada.
Many type transformations are illustrated in
this program and should be easy for you to understand.  Note
especially, that the final result of lines 27, 28, and 29 will not
necessarily be the same due to the rounding that takes place at
different points in the calculations.

Note that in Ada, conversion from real to integer always rounds
rather than truncates.  A value midway between the two integer
values can go either way since it is not defined by the LRM but is
left up to the implementor.



                                                        Page 6-11

                              Chapter 6 - Additional Scalar Types

Compile and execute this program to assure yourself that it will
compile correctly.


PROGRAMMING EXERCISES
_________________________________________________________________

1.   Write a program to determine if LONG_INTEGER and SHORT_INTEGER
     types are available with your compiler.  If they are
     available, use attributes to determine their characteristics.

2.   Do the same thing as exercise 1 for the LONG_FLOAT and
     SHORT_FLOAT types.

3.   Try to take the PRED of the first element of an enumerated
     variable to see what kind of a run-time error you get.  Your
     compiler may be smart enough to warn you if you try to take
     it directly (i.e. - by using the first value in the
     parentheses), so you may need to assign a variable to the
     first value and take the PRED of the variable.

































                                                        Page 6-12