Contents

2.9 Non-existent FIXED_UNITS package

Since I have written an INTEGER_UNITS package and a FLOAT_UNITS package it is reasonable to expect me to have written a FIXED_UNITS package. I haven't. There were two reasons why not.

First, the FIXED_UNITS package requires some options that can't easily be selected using generic parameters. Sure, I could use generic parameters for the number of digits, minimum, and maximum values, but there are more decisions that need to be made. For example, sooner or later you will have to convert the value to a character string of many digits. Do you want to separate digits by commas, periods, or underlines? There are even more difficult decisions to be made regarding the location of the decimal point after arithmetic operations. I just couldn't guess what you would want, so I didn't try.

Second, I don't think the FIXED_UNITS package is necessary, and it might not even be a good idea. If I wrote a FIXED_UNITS package and included it with INTEGER_UNITS and FLOAT_UNITS, then it would encourage people to use FIXED_UNITS when they probably should use INTEGER_UNITS or FLOAT_UNITS. I don't like to encourage the use of fixed- point arithmetic.

2.9.1 Problems with Fixed-Point

You might be tempted to use Ada's predefined fixed point type to represent certain kinds of physical quantities, like dollars. That's not a good idea. Suppose you write type Dollars is delta 0.01 range -10_000_000.00 .. 10_000_000.00;. You might be surprised to discover that the accuracy isn't exactly one cent. The LRM allows Dollars'SMALL to be 1/100 or 1/128 (or any other fraction smaller than 1/100). If it uses 1/128, then there could be a few cents roundoff error if a long column of numbers is added.

There is an optional pragma you could use to specify Dollars'SMALL, but it might not be supported on the compiler you want to use. Even if it is, whenever you depend on pragmas to make your program work, you are asking for portability problems.

The LRM gives the vendors so much freedom when it comes to implementing fixed-point types, you can't be sure what you are getting. DEC Ada just uses a 32 bit integer, and interprets the LSB as the delta value. That means DEC Ada could represent the type Dollars described above perfectly, but could not represent Big_Bucks if type Big_Bucks is delta 0.01 range -22_000_000.00 .. 22_000_000.00; because that exceeds the range of values that can be represented by 32 bits.

I have never found a situation where Ada's fixed-point data type was useful. If I need nine or fewer digits of range, with one digit of absolute accuracy, then I can use Integer_32. Just for the sake of argument, suppose there was a situation where I needed 50 digits of range with one digit of accuracy, then the implementation of Ada's fixed-point type probably wouldn't be sufficient to support that many digits anyway. Even if it did, I couldn't be sure I would get the accurate results from it.

2.9.2 Custom Fixed-Point Types

If I did have an application where I needed exceptionally high accuracy, I would write a package that used a private data type to represent the large, fixed-point numbers. It's so easy to do, I usually assign it as a problem in my beginning Ada class. There are three ways students commonly solve the problem.

Most students use the same Binary Coded Decimal (BCD) approach that 4-bit hand-held calculators use. This involves creating an array of digits that represents the number. Arithmetic operators process the numbers one digit at a time, just like you would if you were adding two long numbers using paper and pencil. They use hidden variables with names like CARRY, BORROW, and PARTIAL_PRODUCT to hold some intermediate results. A boolean variable tells if the value is positive or negative, and another integer tells where the decimal point is.

It isn't very efficient to use a 16- or 32-bit computer to process numbers 4 bits at a time, so some students use an integer to represent a group of three or four digits. The technique is basically the same as BCD, but the computer does several digits at a time, so it is more efficient.

Perhaps the most difficult part of these two approaches is converting arrays of digits to character strings, and vice versa. This sometimes leads a third group of students to use a character string as the private type representing the long number. They still process the data one digit at a time, but instead of arrays of integers they use arrays of characters and define special operations that work on characters.

2.9.3 FIXED_UNITS exercise

If you like, you can try to write a FIXED_UNITS package. Start with the INTEGER_UNITS specification and make a few modifications. (You don't have to make it generic if you don't want to.) You will have to replace the definition of the private type with something that can represent many digits. You can use one of the three suggested approaches above, or come up with something totally different. You will probably want to replace the Type_Convert and Dimensionless functions with Image and Value functions (similar to those found in the ASCII_UTILITIES package in the next section), because you can't use numeric types to represent all the values you want to represent. (If you could, you wouldn't need the FIXED_UNITS package.)

If you want to be able to multiply a monetary figure (which has two decimal places) by 6.5% sales tax (three decimal places), then your private type will have to have a "floating decimal point" so it can represent numbers to two or three (or any other number) or decimal places.

If you allow a floating decimal point it raises some interesting problems. For example, If you subtract 0.123 from 0.9, you will have to move the decimal point before you find the difference. Once you have performed the subtraction, should the answer be 0.777 or 0.8?

The problems with fixed-point arithmetic make it more trouble than it is worth. I think you would be better off to avoid fixed-point arithmetic whenever you can.

2.9.4 Fixed-Point Computers

Ten or fifteen years ago, before floating-point coprocessors were common, some computers were designed to use fixed-point binary numbers. A fixed-point computer does surprising things if you don't know how it works. For example, a computer that uses integer arithmetic will multiply 2#0100# times 2#0010# and yield the expected result 2#01000# (4 * 2 = 8). When a fixed-point computer multiplies 2#0100# times 2#0010# the answer is 2#00001# (1/4 * 1/8 = 1/32). That's because integer machines assume a binary point to the right of all the bits, while a fixed-point machine assumes a binary point to the left of all the bits.

Fixed-point computers are no longer in vogue, but they are still in some military systems. An Ada compiler for a fixed-point target computer might take advantage of certain machine features if you declare real data types to be fixed- point types with deltas that are a power of 2. I haven't used an Ada compiler for a fixed-point computer, so I don't know what it does, but you may want to look into it you find yourself in that situation.


Contents | Next ...