Contents

2.6 Generic FLOAT_UNITS package

The FLOAT_UNITS package, shown in Listing 4, is almost identical to the INTEGER_UNITS package. Therefore most of the comments about the INTEGER_UNITS package hold equally true for the FLOAT_UNITS package. Let's just concentrate on the differences. First, Listing 4 is instantiated for a type described as digits <> instead of range <>. This means we can instantiate it for any real type.

Second, the limits MIN and MAX are real numbers instead of integers. I chose to give MIN and MAX fixed defaults rather than an attribute. I gave serious consideration to letting the defaults be -1.0 * SAFE_LARGE and SAFE_LARGE, and finally decided against it. There was no compelling argument to make a decision one way or another, and I finally picked +/- 1.0e25 as the limits because I felt they will cover most real applications and they are less than the published limits for the type float for all the compilers I use. If you need an exceptionally large range you can always instantiate this package with any limits the compiler can support. Using fixed values instead of attributes guarantees that the default limits will be the same no matter which compiler is used, so it is less likely that there will be an unpleasant surprise when programs are moved from one computer to another.

2.6.1 Division, Remainder, and Modulo Operators

Normally if you take the ratio of two real numbers, you want a real result. There are times, however, when you want the old elementary school definition of division. ("How many times does X go into Y?") For example, "How many complete revolutions has an object made if it has turned 25.89 radians?" You might also want to know the orientation of an object that has turned 25.89 radians. A division operator that returns an integer ratio, and a modulo operator that takes a real argument, would be helpful in converting 25.89 radians to 4 revolutions and 0.76 radians. Ada does not define division of two real numbers with an integer result, nor does she have rem and mod operators for real types. That's not a major problem. It was easily solved.

I started with INTEGER_UNITS package and converted integer types to floating point types. I left the two common division operators (function "/"(LEFT : Units; RIGHT : Float_type) return Units; and function "/"(LEFT, RIGHT : Units) return Float_type;) clustered with the other common arithmetic operators, but simply for cosmetic reasons I moved function "/"(LEFT, RIGHT : Units) return Integer_type; to the end of the list of arithmetic operators, next to the mod and rem. (It put the division operator close to the comment that describes its use.) Then I wrote the bodies of the integer division, rem, and mod operators. (INTEGER_UNITS just used the predefined Ada operators. Since the equivalent operators don't exist for real types, I had to write them myself for FLOAT_UNITS.)

When I first wrote this package I used integer for the result of the integer ratio. That was a bad idea because, as the broken record says, the range of integer depends on the implementation. Then I decided to use STANDARD_INTEGERS.Integer_32. That wasn't a good idea either, because I don't expect ratios to be large and it seems silly to burden a 16 bit computer with double precision arithmetic to calculate a number less than 100.

I finally did the smart thing. I added a fourth generic parameter, Integer_type, and let the programmer make the best choice based on the application.

2.6.2 First and Last Functions

When I talked about the INTEGER_UNITS package I intentionally avoided mentioning the First and Last functions because I wanted to wait until now to discuss them. Both INTEGER_UNITS and FLOAT_UNITS have First and Last functions.

If you have done much Ada programming I'm sure you've found how valuable the FIRST and LAST attributes are. You have probably come to take them for granted. If I didn't provided you the First and Last functions, it would be distressing to discover that a dimensioned data type, such as Celsius, has no FIRST or LAST attributes.

There are two reasons why Ada won't allow you to write a statement like ABSOLUTE_ZERO := Celsius'FIRST;. The first is that even if type Celsius is new DIM_INT_32.Units; then type Celsius is a private type, not an integer type. The fact that type Units is represented by a 32 bit integer doesn't matter. Private types don't have FIRST and LAST attributes no matter how they are represented.

The second reason is that if type Celsius is new DIM_FLOAT.Units; then type Celsius is a private type that is represented by a type (float) that doesn't have FIRST and LAST attributes to begin with!

The First and Last functions provide a way of obtaining the MIN and MAX values used to instantiate the INTEGER_UNITS or FLOAT_UNITS package. They aren't quite as clean as actual attributes, but at least it is possible to declare ABSOLUTE_ZERO : constant Celsius := Type_Convert(TEMPERATURE.First); (regardless of whether TEMPERATURE is an instantiation of INTEGER_UNITS or FLOAT_UNITS).


Contents | Next ...