Ada Programming/Mathematical calculations
Template:Ada/Navigation 1 Computer programming/Mathematical calculations/0
Ada is very well suited for all kind of calculations. You can define you own fixed point and floating point types and — with the aid of generic packages call all the mathematical functions you need. In that respect Ada is on par with Fortran. This module will show you how to use them and while we progress we create a simple RPN calculator.
Simple calculations
Additions can be done using the predefined operator Template:Ada/operator. The operator is predefined for all numeric types and the following, working code, demonstrates its use:
Template:Ada/Sourceforge Template:Ada/-- Template:Ada/kw Template:Ada/package 2; Template:Ada/kw Numeric_1 Template:Ada/kw Template:Ada/kw Value_Type Template:Ada/kw Template:Ada/kw 12 Template:Ada/kw -999_999_999_999.0e999 .. 999_999_999_999.0e999; Template:Ada/kw T_IO Template:Ada/kw Ada.Text_IO; Template:Ada/kw F_IO Template:Ada/kw Template:Ada/kw Ada.Text_IO.Float_IO (Value_Type); Value_1 : Value_Type; Value_2 : Value_Type; Template:Ada/kw T_IO.Put ("First Value : "); F_IO.Get (Value_1); T_IO.Put ("Second Value : "); F_IO.Get (Value_2); F_IO.Put (Value_1); T_IO.Put (" + "); F_IO.Put (Value_2); T_IO.Put (" = "); F_IO.Put (Value_1 Template:Ada/operator Value_2); Template:Ada/kw Numeric_1;
Subtractions can be done using the predefined operator Template:Ada/operator. The following extended demo shows the use of + and - operator together:
Template:Ada/Sourceforge Template:Ada/-- Template:Ada/kw Template:Ada/package 2Template:Ada/special Template:Ada/kw Numeric_2 Template:Ada/kw Template:Ada/kw Value_Type Template:Ada/kw Template:Ada/kw 12 Template:Ada/kw -999_999_999_999.0e999 .. 999_999_999_999.0e999; Template:Ada/kw T_IO Template:Ada/kw Template:Ada/package 2; Template:Ada/kw F_IO Template:Ada/kw Template:Ada/kw Ada.Text_IO.Float_IO (Value_Type); Value_1 : Value_Type; Value_2 : Value_Type; Result : Value_Type; Operation : Character; Template:Ada/kw T_IO.Put ("First Value : "); F_IO.Get (Value_1); T_IO.Put ("Second Value : "); F_IO.Get (Value_2); T_IO.Put ("Operation : "); T_IO.Get (Operation); Template:Ada/kw Operation Template:Ada/kw Template:Ada/kw '+' => Result := Value_1 + Value_2; Template:Ada/kw '-' => Result := Value_1 - Value_2; Template:Ada/kw Template:Ada/kw => T_IO.Put_Line ("Illegal Operation."); Template:Ada/kw Exit_Numeric_2; Template:Ada/kw Template:Ada/kw; F_IO.Put (Value_1); T_IO.Put (" "); T_IO.Put (Operation); T_IO.Put (" "); F_IO.Put (Value_2); T_IO.Put (" = "); F_IO.Put (Result); Template:Ada/delimiter 2Exit_Numeric_2Template:Ada/delimiter 2 Template:Ada/kw; Template:Ada/kw Numeric_2;
Purists might be surprised about the use of goto - but some people prefer the use of goto over the use of multiple return statements if inside functions - given that, the opinions on this topic vary strongly. See the isn't goto evil article.
Multiplication can be done using the predefined operator Template:Ada/operator. For a demo see the next chapter about Division.
Divisions can be done using the predefined operators Template:Ada/operator, Template:Ada/operator, Template:Ada/operator. The operator Template:Ada/operator performs a normal division, Template:Ada/operator returns a modulus division and Template:Ada/operator returns the remainder of the modulus division.
The following extended demo shows the use of the Template:Ada/operator, Template:Ada/operator, Template:Ada/operator and Template:Ada/operator operators together as well as the use of an four number wide stack to store intermediate results:
The operators Template:Ada/operator and Template:Ada/operator are not part of the demonstration as they are only defined for integer types.
Template:Ada/Sourceforge Template:Ada/kw Template:Ada/package 2; Template:Ada/kw Numeric_3 Template:Ada/kw Template:Ada/kw Pop_Value; Template:Ada/kw Push_Value; Template:Ada/kw Value_Type Template:Ada/kw Template:Ada/kw 12 Template:Ada/kw -999_999_999_999.0e999 .. 999_999_999_999.0e999; Template:Ada/kw Value_Array Template:Ada/kw Template:Ada/kw (Natural Template:Ada/kw 1 .. 4) Template:Ada/kw Value_Type; Template:Ada/kw T_IO Template:Ada/kw Ada.Text_IO; Template:Ada/kw F_IO Template:Ada/kw Template:Ada/kw Ada.Text_IO.Float_IO (Value_Type); Values : Value_Array := (Template:Ada/kw => 0.0); Operation : String (1 .. 40); Last : Natural; Template:Ada/kw Pop_Value Template:Ada/kw Template:Ada/kw Values (Values'Template:Ada/attribute + 1 .. Values'Template:Ada/attribute) := Values (Values'Template:Ada/attribute + 2 .. Values'Template:Ada/attribute) & 0.0; Template:Ada/kw Pop_Value; Template:Ada/kw Push_Value Template:Ada/kw Template:Ada/kw Values (Values'Template:Ada/attribute + 1 .. Values'Template:Ada/attribute) := Values (Values'Template:Ada/attribute .. Values'Template:Ada/attribute - 1); Template:Ada/kw Push_Value; Template:Ada/kw Main_Loop: Template:Ada/kw T_IO.Put (">"); T_IO.Get_Line (Operation, Last); Template:Ada/kw Last = 1 Template:Ada/kw Template:Ada/kw Operation (1) = '+' Template:Ada/kw Values (1) := Values (1) + Values (2); Pop_Value; Template:Ada/kw Last = 1 Template:Ada/kw Template:Ada/kw Operation (1) = '-' Template:Ada/kw Values (1) := Values (1) + Values (2); Pop_Value; Template:Ada/kw Last = 1 Template:Ada/kw Template:Ada/kw Operation (1) = '*' Template:Ada/kw Values (1) := Values (1) * Values (2); Pop_Value; Template:Ada/kw Last = 1 Template:Ada/kw Template:Ada/kw Operation (1) = '/' Template:Ada/kw Values (1) := Values (1) / Values (2); Pop_Value; Template:Ada/kw Last = 4 Template:Ada/kw Template:Ada/kw Operation (1 .. 4) = "exit" Template:Ada/kw Template:Ada/kw Main_Loop; Template:Ada/kw Push_Value; F_IO.Get (From => Operation, Item => Values (1), Last => Last); Template:Ada/kw Template:Ada/kw; Display_Loop: Template:Ada/kw I Template:Ada/kw Template:Ada/kw Value_Array'Template:Ada/attribute Template:Ada/kw F_IO.Put (Item => Values (I), Fore => F_IO.Default_Fore, Aft => F_IO.Default_Aft, Exp => 4); T_IO.New_Line; Template:Ada/kw Template:Ada/kw Display_Loop; Template:Ada/kw Template:Ada/kw Main_Loop; Template:Ada/kw; Template:Ada/kw Numeric_3;
Exponential calculations
All exponential functions are defined inside the generic package Template:Ada/package 3.
Calculation of the form are performed by the operator Template:Ada/operator. Beware: There are two versions of this operator. The predefined operator Template:Ada/operator allows only for Standard.Integer to be used as exponent. If you need to use a floating point type as exponent you need to use the Template:Ada/operator defined in Template:Ada/package 3.
The square root is calculated with the function Sqrt(). There is no function defined to calculate an arbitrary root . However you can use logarithms to calculate an arbitrary root using the mathematical identity: which will become root := Exp (Log (a) / b) in Ada. Alternatively, use which, in Ada, is root := a**(1.0/b).
Template:Ada/package 3 defines a function for both the arbitrary logarithm and the natural logarithm both of of which have the same name Log() distinguished by the amount of parameters.
Demonstration
The following extended demo shows the how to use the exponential functions in Ada. The new demo also uses Unbounded_String instead of Strings which make the comparisons easier.
Please note that from now on we won't copy the full sources any more. Do follow the download links to see the full program.
Template:Ada/Sourceforge
Template:Ada/kw Template:Ada/package 2;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Numeric_4 Template:Ada/kw
Template:Ada/kw Str Template:Ada/kw Ada.Strings.Unbounded;
Template:Ada/kw T_IO Template:Ada/kw Ada.Text_IO;
Template:Ada/kw Pop_Value;
Template:Ada/kw Push_Value;
Template:Ada/kw Get_Line Template:Ada/kw Str.Unbounded_String;
Template:Ada/kw Value_Type Template:Ada/kw Template:Ada/kw 12 Template:Ada/kw
-999_999_999_999.0e999 .. 999_999_999_999.0e999;
Template:Ada/kw Value_Array Template:Ada/kw Template:Ada/kw (Natural Template:Ada/kw 1 .. 4) Template:Ada/kw Value_Type;
Template:Ada/kw F_IO Template:Ada/kw Template:Ada/kw Ada.Text_IO.Float_IO (Value_Type);
Template:Ada/kw Value_Functions Template:Ada/kw Template:Ada/kw Ada.Numerics.Generic_Elementary_Functions (
Value_Type);
Template:Ada/kw Value_Functions;
Template:Ada/kw Template:Ada/kw Str.Unbounded_String;
Values : Value_Array := (Template:Ada/kw => 0.0);
Operation : Str.Unbounded_String;
Dummy : Natural;
Template:Ada/kw Get_Line Template:Ada/kw Str.Unbounded_String Template:Ada/kw
BufferSize : Template:Ada/kw := 2000;
Retval : Str.Unbounded_String := Str.Null_Unbounded_String;
Item : String (1 .. BufferSize);
Last : Natural;
Template:Ada/kw
Get_Whole_Line :
Template:Ada/kw
T_IO.Get_Line (Item => Item, Last => Last);
Str.Append (Source => Retval, New_Item => Item (1 .. Last));
Template:Ada/kw Get_Whole_Line Template:Ada/kw Last < Item'Template:Ada/attribute;
Template:Ada/kw Template:Ada/kw Get_Whole_Line;
Template:Ada/kw Retval;
Template:Ada/kw Get_Line;
...
Template:Ada/kw
Main_Loop :
Template:Ada/kw
T_IO.Put (">");
Operation := Get_Line;
...
Template:Ada/kw Operation = "e" Template:Ada/kw
-- insert e
Push_Value;
Values (1) := Ada.Numerics.e;
Template:Ada/kw Operation = "**" Template:Ada/kw Template:Ada/kw Operation = "^" Template:Ada/kw
-- power of x^y
Values (1) := Values (1) ** Values (2);
Pop_Value;
Template:Ada/kw Operation = "sqr" Template:Ada/kw
-- square root
Values (1) := Sqrt (Values (1));
Template:Ada/kw Operation = "root" Template:Ada/kw
-- arbritary root
Values (1) :=
Exp (Log (Values (2)) / Values (1));
Pop_Value;
Template:Ada/kw Operation = "ln" Template:Ada/kw
-- natural logarithm
Values (1) := Log (Values (1));
Template:Ada/kw Operation = "log" Template:Ada/kw
-- based logarithm
Values (1) :=
Log (Base => Values (1), X => Values (2));
Pop_Value;
Template:Ada/kw Operation = "Template:Ada/kw" Template:Ada/kw
Template:Ada/kw Main_Loop;
Template:Ada/kw
Push_Value;
F_IO.Get
(From => Str.To_String (Operation),
Item => Values (1),
Last => Dummy);
Template:Ada/kw Template:Ada/kw;
...
Template:Ada/kw Template:Ada/kw Main_Loop;
Template:Ada/kw;
Template:Ada/kw Numeric_4;
Higher math
Trigonometric calculations
The full set of trigonometric functions are defined inside the generic package Template:Ada/package 3. All functions are defined for 2π and an arbitrary cycle value (a full cycle of revolution).
Please note the difference of calling the Arctan () function.
Template:Ada/kw Template:Ada/package 2;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Numeric_5 Template:Ada/kw
...
Template:Ada/kw Put_Line (Value : Template:Ada/kw Value_Type);
Template:Ada/kw Value_Functions;
Template:Ada/kw Template:Ada/kw Str.Unbounded_String;
Values : Value_Array := (Template:Ada/kw => 0.0);
Cycle : Value_Type := Ada.Numerics.Pi;
Operation : Str.Unbounded_String;
Dummy : Natural;
...
Template:Ada/kw Put_Line (Value : Template:Ada/kw Value_Type) Template:Ada/kw
Template:Ada/kw
Template:Ada/kw Template:Ada/kw Value_Type'Template:Ada/attribute (Value) >=
Template:Ada/kw Value_Type'Template:Ada/attribute (10.0 ** F_IO.Default_Aft)
Template:Ada/kw
F_IO.Put
(Item => Value,
Fore => F_IO.Default_Aft,
Aft => F_IO.Default_Aft,
Exp => 4);
Template:Ada/kw
F_IO.Put
(Item => Value,
Fore => F_IO.Default_Aft,
Aft => F_IO.Default_Aft,
Exp => 0);
Template:Ada/kw Template:Ada/kw;
T_IO.New_Line;
Template:Ada/kw;
Template:Ada/kw Put_Line;
...
Template:Ada/kw
Main_Loop :
Template:Ada/kw
Display_Loop :
Template:Ada/kw I Template:Ada/kw Template:Ada/kw Value_Array'Template:Ada/attribute Template:Ada/kw
Put_Line (Values (I));
Template:Ada/kw Template:Ada/kw Display_Loop;
T_IO.Put (">");
Operation := Get_Line;
...
Template:Ada/kw Operation = "deg" Template:Ada/kw
-- switch to degrees
Cycle := 360.0;
Template:Ada/kw Operation = "rad" Template:Ada/kw
-- switch to degrees
Cycle := Ada.Numerics.Pi;
Template:Ada/kw Operation = "grad" Template:Ada/kw
-- switch to degrees
Cycle := 400.0;
Template:Ada/kw Operation = "pi" Template:Ada/kw Template:Ada/kw Operation = "π" Template:Ada/kw
-- switch to degrees
Push_Value;
Values (1) := Ada.Numerics.Pi;
Template:Ada/kw Operation = "sin" Template:Ada/kw
-- sinus
Values (1) := Sin (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "cos" Template:Ada/kw
-- cosinus
Values (1) := Cos (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "tan" Template:Ada/kw
-- tangents
Values (1) := Tan (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "cot" Template:Ada/kw
-- cotanents
Values (1) := Cot (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "asin" Template:Ada/kw
-- arc-sinus
Values (1) := Arcsin (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "acos" Template:Ada/kw
-- arc-cosinus
Values (1) := Arccos (X => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "atan" Template:Ada/kw
-- arc-tangents
Values (1) := Arctan (Y => Values (1), Cycle => Cycle);
Template:Ada/kw Operation = "acot" Template:Ada/kw
-- arc-cotanents
Values (1) := Arccot (X => Values (1), Cycle => Cycle);
...
Template:Ada/kw Template:Ada/kw Main_Loop;
Template:Ada/kw;
Template:Ada/kw Numeric_5;
The Demo also contains an improved numeric output which behaves more like a normal calculator.
Hyperbolic calculations
You guessed it: The full set of hyperbolic functions is defined inside the generic package Template:Ada/package 3.
Template:Ada/Sourceforge
Template:Ada/kw Template:Ada/package 2;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Template:Ada/package 3;
Template:Ada/kw Template:Ada/package 2;
Template:Ada/kw Numeric_6 Template:Ada/kw
Template:Ada/kw Str Template:Ada/kw Ada.Strings.Unbounded;
Template:Ada/kw T_IO Template:Ada/kw Ada.Text_IO;
Template:Ada/kw Exept Template:Ada/kw Ada.Exceptions;
...
Template:Ada/kw
Main_Loop :
Template:Ada/kw
Try :
Template:Ada/kw
Display_Loop :
...
Template:Ada/kw Operation = "sinh" Template:Ada/kw
-- sinus hyperbolic
Values (1) := Sinh (Values (1));
Template:Ada/kw Operation = "cosh" Template:Ada/kw
-- cosinus hyperbolic
Values (1) := Coth (Values (1));
Template:Ada/kw Operation = "tanh" Template:Ada/kw
-- tangents hyperbolic
Values (1) := Tanh (Values (1));
Template:Ada/kw Operation = "coth" Template:Ada/kw
-- cotanents hyperbolic
Values (1) := Coth (Values (1));
Template:Ada/kw Operation = "asinh" Template:Ada/kw
-- arc-sinus hyperbolic
Values (1) := Arcsinh (Values (1));
Template:Ada/kw Operation = "acosh" Template:Ada/kw
-- arc-cosinus hyperbolic
Values (1) := Arccosh (Values (1));
Template:Ada/kw Operation = "atanh" Template:Ada/kw
-- arc-tangents hyperbolic
Values (1) := Arctanh (Values (1));
Template:Ada/kw Operation = "acoth" Template:Ada/kw
-- arc-cotanents hyperbolic
Values (1) := Arccoth (Values (1));
...
Template:Ada/kw
Template:Ada/kw An_Exception : Template:Ada/kw =>
T_IO.Put_Line
(Exept.Exception_Information (An_Exception));
Template:Ada/kw Try;
Template:Ada/kw Template:Ada/kw Main_Loop;
Template:Ada/kw;
Template:Ada/kw Numeric_6;
As added bonus this version supports error handling and therefore won't just crash when an illegal calculation is performed.
Complex arithmethic
For complex arithmetic Ada provides the package Template:Ada/package 3. This package is part of the "special need Annexes" which means it is optional. The open source Ada compiler GNAT implements all "special need Annexes" and therefore has complex arithmetic available.
Since Ada support user defined operators all (Template:Ada/operator, Template:Ada/operator, Template:Ada/operator) operators have there usual meaning as soon as the package Template:Ada/package 3 has been instantiated (Template:Ada/kw ... Template:Ada/kw Template:Ada/kw ...) and the type has been made visible (Template:Ada/kw Template:Ada/kw ...)
Ada also provides the packages Template:Ada/package 3 and Template:Ada/package 3 which provide similar functionality to there normal counterparts. But there are some differences:
- Template:Ada/package 3 supports only the exponential and trigonometric functions which make sense in complex arithmetic.
- Template:Ada/package 3 is a child package of Template:Ada/package 2 and therefore needs it's own Template:Ada/kw. Note: the
Template:Ada/package 3Get () function is pretty fault tolerant - if you forget the "," or the "()" pair it will still parse the input correctly.
So, with only a very few modifications you can convert your "normals" calculator to a calculator for complex arithmetic:
Template:Ada/kw Ada.Text_IO.Complex_IO;
Template:Ada/kw Ada.Numerics.Generic_Complex_Types;
Template:Ada/kw Ada.Numerics.Generic_Complex_Elementary_Functions;
Template:Ada/kw Ada.Strings.Unbounded;
Template:Ada/kw Ada.Exceptions;
Template:Ada/kw Numeric_7 Template:Ada/kw
...
Template:Ada/kw Complex_Types Template:Ada/kw Template:Ada/kw Ada.Numerics.Generic_Complex_Types (
Value_Type);
Template:Ada/kw Complex_Functions Template:Ada/kw Template:Ada/kw
Ada.Numerics.Generic_Complex_Elementary_Functions (
Complex_Types);
Template:Ada/kw C_IO Template:Ada/kw Template:Ada/kw Ada.Text_IO.Complex_IO (Complex_Types);
Template:Ada/kw Value_Array Template:Ada/kw
Template:Ada/kw (Natural Template:Ada/kw 1 .. 4) Template:Ada/kw Complex_Types.Complex;
Template:Ada/kw Put_Line (Value : Template:Ada/kw Complex_Types.Complex);
Template:Ada/kw Template:Ada/kw Complex_Types.Complex;
Template:Ada/kw Template:Ada/kw Str.Unbounded_String;
Template:Ada/kw Complex_Functions;
Values : Value_Array :=
(Template:Ada/kw => Complex_Types.Complex'(Re => 0.0, Im => 0.0));
...
Template:Ada/kw Put_Line (Value : Template:Ada/kw Complex_Types.Complex) Template:Ada/kw
Template:Ada/kw
Template:Ada/kw (Template:Ada/kw Value_Type'Template:Ada/attribute (Value.Re) >=
Template:Ada/kw Value_Type'Template:Ada/attribute (10.0 ** C_IO.Default_Aft))
Template:Ada/kw Template:Ada/kw (Template:Ada/kw Value_Type'Template:Ada/attribute (Value.Im) >=
Template:Ada/kw Value_Type'Template:Ada/attribute (10.0 ** C_IO.Default_Aft))
Template:Ada/kw
C_IO.Put
(Item => Value,
Fore => C_IO.Default_Aft,
Aft => C_IO.Default_Aft,
Exp => 4);
Template:Ada/kw
C_IO.Put
(Item => Value,
Fore => C_IO.Default_Aft,
Aft => C_IO.Default_Aft,
Exp => 0);
Template:Ada/kw Template:Ada/kw;
T_IO.New_Line;
Template:Ada/kw;
Template:Ada/kw Put_Line;
Template:Ada/kw
...
Template:Ada/kw Operation = "e" Template:Ada/kw
-- insert e
Push_Value;
Values (1) :=
Complex_Types.Complex'(Re => Ada.Numerics.e, Im => 0.0);
...
Template:Ada/kw Operation = "pi" Template:Ada/kw Template:Ada/kw Operation = "π" Template:Ada/kw
-- insert pi
Push_Value;
Values (1) :=
Complex_Types.Complex'(Re => Ada.Numerics.Pi, Im => 0.0);
Template:Ada/kw Operation = "sin" Template:Ada/kw
-- sinus
Values (1) := Sin (Values (1));
Template:Ada/kw Operation = "cos" Template:Ada/kw
-- cosinus
Values (1) := Cot (Values (1));
Template:Ada/kw Operation = "tan" Template:Ada/kw
-- tangents
Values (1) := Tan (Values (1));
Template:Ada/kw Operation = "cot" Template:Ada/kw
-- cotanents
Values (1) := Cot (Values (1));
Template:Ada/kw Operation = "asin" Template:Ada/kw
-- arc-sinus
Values (1) := Arcsin (Values (1));
Template:Ada/kw Operation = "acos" Template:Ada/kw
-- arc-cosinus
Values (1) := Arccos (Values (1));
Template:Ada/kw Operation = "atan" Template:Ada/kw
-- arc-tangents
Values (1) := Arctan (Values (1));
Template:Ada/kw Operation = "acot" Template:Ada/kw
-- arc-cotanents
Values (1) := Arccot (Values (1));
...
Template:Ada/kw;
Template:Ada/kw Numeric_7;
Ada supports vector and matrix Arithmetic for both normal real types and complex types. For those, the generic packages Ada.Numerics.Generic_Real_Arrays and Ada.Numerics.Generic_Complex_Arrays are used. Both packages offer the usual set of operations, however there is no I/O package and understandably, no package for elementary functions.
Since there is no I/O package for vector and matrix I/O creating a demo is by far more complex — and hence not ready yet. You can have a look at the current progress which will be a universal calculator merging all feature.
Status: Stalled - for a Vector and Matrix stack we need Indefinite_Vectors — which are currently not part of GNAT/Pro. Well I could use the booch components ...
Template:Ada/Sourceforge
Template:Ada/Sourceforge
Template:Ada/Sourceforge
Template:Ada/Sourceforge
See also
Wikibook
Ada 95 Reference Manual
Ada 2005 Reference Manual