Wednesday, September 19, 2012

AdaTutor - Advanced Topics (2)

More Attributes

Ada provides many attributes, all described in Annex K of the Ada 95 RM.  The most important ones that we haven't yet discussed are these:

For any real type or subtype (floating or fixed), 'Small and 'Large are the smallest and largest positive model numbers.  Thus Float'Small is the difference between zero and the next larger number in type Float.  Also, for any floating point (sub)type, 'Model_Epsilon is the difference between one and the next larger number.  (In Ada 83, 'Model_Epsilon is simply called 'Epsilon.)  We'll use 'Epsilon in an Ada 83 generic function in a moment.

For a floating point (sub)type, 'Digits returns the value given for digits in the declaration, and for a fixed point (sub)type, 'Delta returns the value given for delta in the declaration.  These attributes may not seem too useful, because the programmer already knows what he or she wrote in the declarations.  However, they're useful in generic packages and subprograms.  For example, if the generic part says type Dummy is delta <>;, the body can use Dummy'Delta.

Ada 95 has 'Max and 'Min, both of which take two scalar parameters.  For example, if we have I : Integer := 1; and J : Integer := 2;, then Integer'Max(I, J) is 2.

For any discrete (sub)type, 'Width gives the maximum length that the attribute 'Image can produce.  Boolean'Width is 5 because “False” has length 5.  With our earlier definition of Rainbow_Color, Rainbow_Color'Width is 6.  For versions of Ada using 16-bit Integers, Integer'Width is also 6, the length of &lduqo;-32768”.

'Count is used with the name of a task entry.  It returns the number of calls presently queued on the entry.  'Terminated is of type Boolean.  It's used with a task name, and tells if the task is terminated.

Ada 95 has a package Ada.Numerics.Generic_Elementary_Functions containing a square root (Sqrt) function, but Ada 83 doesn't have this package.  As an exercise, let's write a generic Ada 83 function to compute the square root for any floating point type, using Newton-Raphson iteration.

Let's suppose that G is our guess of the square root of X.  If our guess is correct, then X/G equals G.  If our guess is too low, then X/G is larger than G, and if our guess is too high, then X/G is smaller than G.  The Newton- -Raphson method simply says that our next guess is the average of G and X/G (one of which is too high, and the other of which is too low).  For example, if we want to compute the square root of 9.0 and our first guess is 9.0, successive guesses are 5.0, 3.4, 3.02352941, 3.00009155, 3.00000000.  Note that convergence is very rapid.

However, the problem in writing a program like this is knowing when to stop the iteration.  We'll use the Ada 83 attribute 'Epsilon (called 'Model_Epsilon in Ada 95).  Since G*G/X should be 1.0, we'll quit when the absolute value of the difference between G*G/X and 1.0 is less than or equal to 3.0 times Epsilon.  Recall that Dummy'Epsilon is the difference between 1.0 and the next higher number for type Dummy.  If we use 1.0 times Epsilon, the loop might never terminate, and if we use 10.0 times Epsilon, we might not get full precision.  So we'll use 3.0 times Epsilon.  Here's our function:

   generic
      type Dummy is digits <>;
   function Sqrt(X :in Dummy) return Dummy;
   function Sqrt(X :in Dummy) return Dummy is
      Guess : Dummy := X;
   begin
      if X < 0.0 then
         raise Constraint_Error;
      end if;
      while X /= 0.0 and then abs(Guess*Guess/X - 1.0)
                               > 3.0*Dummy'Epsilon loop
         Guess := (X/Guess + Guess) * 0.5;
      end loop;
      return Guess;
   end Sqrt;

We tested our Sqrt with an implementation of Ada 83 having types Float, Long_Float, and Long_Long_Float.  The last gives at least 33 decimal digits of precision.  Sqrt was instantiated for all three floating point types, as was Float_IO to display the results.  When tested with the three types, all displayed digits of the answers were correct.

< prev   next >

No comments: