1 ------------------------------------------------------------------------------
3 -- GNAT RUNTIME COMPONENTS --
9 -- $Revision: 1.1.16.1 $
11 -- Copyright (C) 1992-2000 Free Software Foundation, Inc. --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
31 -- GNAT was originally developed by the GNAT team at New York University. --
32 -- Extensive contributions were provided by Ada Core Technologies Inc. --
34 ------------------------------------------------------------------------------
36 -- This body is specifically for using an Ada interface to C math.h to get
37 -- the computation engine. Many special cases are handled locally to avoid
38 -- unnecessary calls. This is not a "strict" implementation, but takes full
39 -- advantage of the C functions, e.g. in providing interface to hardware
40 -- provided versions of the elementary functions.
42 -- A known weakness is that on the x86, all computation is done in Double,
43 -- which means that a lot of accuracy is lost for the Long_Long_Float case.
45 -- Uses functions sqrt, exp, log, pow, sin, asin, cos, acos, tan, atan,
46 -- sinh, cosh, tanh from C library via math.h
48 -- This is an adaptation of Ada.Numerics.Generic_Elementary_Functions that
49 -- provides a compatible body for the DEC Math_Lib package.
51 with Ada
.Numerics
.Aux
;
52 use type Ada
.Numerics
.Aux
.Double
;
53 with Ada
.Numerics
; use Ada
.Numerics
;
55 package body Math_Lib
is
57 Log_Two
: constant := 0.69314_71805_59945_30941_72321_21458_17656_80755
;
59 Two_Pi
: constant Real
'Base := 2.0 * Pi
;
60 Half_Pi
: constant Real
'Base := Pi
/ 2.0;
61 Fourth_Pi
: constant Real
'Base := Pi
/ 4.0;
62 Epsilon
: constant Real
'Base := Real
'Base'Epsilon;
63 IEpsilon : constant Real'Base := 1.0 / Epsilon;
65 subtype Double is Aux.Double;
67 DEpsilon : constant Double := Double (Epsilon);
68 DIEpsilon : constant Double := Double (IEpsilon);
70 -----------------------
71 -- Local Subprograms --
72 -----------------------
85 function Exact_Remainder
89 -- Computes exact remainder of A divided by Y
91 function Half_Log_Epsilon return Real;
92 -- Function to provide constant: 0.5 * Log (Epsilon)
98 -- Common code for arc tangent after cyele reduction
100 function Log_Inverse_Epsilon return Real;
101 -- Function to provide constant: Log (1.0 / Epsilon)
103 function Square_Root_Epsilon return Real;
104 -- Function to provide constant: Sqrt (Epsilon)
110 function "**" (A1, A2 : Real) return Real is
116 raise Argument_Error;
119 raise Argument_Error;
126 raise Constraint_Error;
143 Real (Aux.pow (Double (A1), Double (A2)));
148 raise Constraint_Error;
159 function Arccos (A : Real) return Real is
164 raise Argument_Error;
166 elsif abs A < Square_Root_Epsilon then
176 Temp := Real (Aux.acos (Double (A)));
187 function Arccos (A, Cycle : Real) return Real is
192 raise Argument_Error;
194 elsif abs A > 1.0 then
195 raise Argument_Error;
197 elsif abs A < Square_Root_Epsilon then
207 Temp := Arctan (Sqrt (1.0 - A * A) / A, 1.0, Cycle);
210 Temp := Cycle / 2.0 + Temp;
220 function Arccosh (A : Real) return Real is
222 -- Return Log (A - Sqrt (A * A - 1.0)); double valued,
223 -- only positive value returned
224 -- What is this comment ???
227 raise Argument_Error;
229 elsif A < 1.0 + Square_Root_Epsilon then
232 elsif abs A > 1.0 / Square_Root_Epsilon then
233 return Log (A) + Log_Two;
236 return Log (A + Sqrt (A * A - 1.0));
252 -- Just reverse arguments
254 return Arctan (Y, A);
266 -- Just reverse arguments
268 return Arctan (Y, A, Cycle);
275 function Arccoth (A : Real) return Real is
278 raise Constraint_Error;
280 elsif abs A < 1.0 then
281 raise Argument_Error;
283 elsif abs A > 1.0 / Epsilon then
287 return 0.5 * Log ((1.0 + A) / (A - 1.0));
297 function Arcsin (A : Real) return Real is
300 raise Argument_Error;
302 elsif abs A < Square_Root_Epsilon then
312 return Real (Aux.asin (Double (A)));
317 function Arcsin (A, Cycle : Real) return Real is
320 raise Argument_Error;
322 elsif abs A > 1.0 then
323 raise Argument_Error;
335 return Arctan (A / Sqrt (1.0 - A * A), 1.0, Cycle);
342 function Arcsinh (A : Real) return Real is
344 if abs A < Square_Root_Epsilon then
347 elsif A > 1.0 / Square_Root_Epsilon then
348 return Log (A) + Log_Two;
350 elsif A < -1.0 / Square_Root_Epsilon then
351 return -(Log (-A) + Log_Two);
354 return -Log (abs A + Sqrt (A * A + 1.0));
357 return Log (A + Sqrt (A * A + 1.0));
376 raise Argument_Error;
393 return Local_Atan (Y, A);
407 raise Argument_Error;
412 raise Argument_Error;
429 return Local_Atan (Y, A) * Cycle / Two_Pi;
437 function Arctanh (A : Real) return Real is
440 raise Constraint_Error;
442 elsif abs A > 1.0 then
443 raise Argument_Error;
445 elsif abs A < Square_Root_Epsilon then
449 return 0.5 * Log ((1.0 + A) / (1.0 - A));
459 function Cos (A : Real) return Real is
464 elsif abs A < Square_Root_Epsilon then
469 return Real (Aux.Cos (Double (A)));
474 function Cos (A, Cycle : Real) return Real is
479 raise Argument_Error;
485 T := Exact_Remainder (abs (A), Cycle) / Cycle;
494 elsif T = 0.5 or T = -0.5 then
498 return Real (Aux.Cos (Double (T * Two_Pi)));
505 function Cosh (A : Real) return Real is
507 if abs A < Square_Root_Epsilon then
510 elsif abs A > Log_Inverse_Epsilon then
511 return Exp ((abs A) - Log_Two);
514 return Real (Aux.cosh (Double (A)));
518 raise Constraint_Error;
527 function Cot (A : Real) return Real is
530 raise Constraint_Error;
532 elsif abs A < Square_Root_Epsilon then
536 return Real (1.0 / Real'Base (Aux.tan (Double (A))));
541 function Cot (A, Cycle : Real) return Real is
546 raise Argument_Error;
549 raise Constraint_Error;
551 elsif abs A < Square_Root_Epsilon then
555 T := Exact_Remainder (A, Cycle) / Cycle;
557 if T = 0.0 or T = 0.5 or T = -0.5 then
558 raise Constraint_Error;
560 return Cos (T * Two_Pi) / Sin (T * Two_Pi);
568 function Coth (A : Real) return Real is
571 raise Constraint_Error;
573 elsif A < Half_Log_Epsilon then
576 elsif A > -Half_Log_Epsilon then
579 elsif abs A < Square_Root_Epsilon then
583 return Real (1.0 / Real'Base (Aux.tanh (Double (A))));
586 ---------------------
587 -- Exact_Remainder --
588 ---------------------
590 function Exact_Remainder
595 Denominator : Real'Base := abs A;
596 Divisor : Real'Base := abs Y;
598 Sign : Real'Base := 1.0;
602 raise Constraint_Error;
610 elsif Denominator < Divisor then
614 while Denominator >= Divisor loop
616 -- Put divisors mantissa with denominators exponent to make reducer
621 while Reducer * 1_048_576.0 < Denominator loop
622 Reducer := Reducer * 1_048_576.0;
630 while Reducer * 1_024.0 < Denominator loop
631 Reducer := Reducer * 1_024.0;
639 while Reducer * 2.0 < Denominator loop
640 Reducer := Reducer * 2.0;
647 Denominator := Denominator - Reducer;
661 function Exp (A : Real) return Real is
669 Result := Real (Aux.Exp (Double (A)));
671 -- The check here catches the case of Exp returning IEEE infinity
673 if Result > Real'Last then
674 raise Constraint_Error;
681 ----------------------
682 -- Half_Log_Epsilon --
683 ----------------------
685 -- Cannot precompute this constant, because this is required to be a
686 -- pure package, which allows no state. A pity, but no way around it!
688 function Half_Log_Epsilon return Real is
690 return Real (0.5 * Real'Base (Aux.Log (DEpsilon)));
691 end Half_Log_Epsilon;
703 Raw_Atan : Real'Base;
706 if abs Y > abs A then
712 if Z < Square_Root_Epsilon then
716 Raw_Atan := Pi / 4.0;
718 elsif Z < Square_Root_Epsilon then
722 Raw_Atan := Real'Base (Aux.Atan (Double (Z)));
725 if abs Y > abs A then
726 Raw_Atan := Half_Pi - Raw_Atan;
738 return Pi - Raw_Atan;
740 return -(Pi - Raw_Atan);
751 function Log (A : Real) return Real is
754 raise Argument_Error;
757 raise Constraint_Error;
763 return Real (Aux.Log (Double (A)));
768 function Log (A, Base : Real) return Real is
771 raise Argument_Error;
773 elsif Base <= 0.0 or else Base = 1.0 then
774 raise Argument_Error;
777 raise Constraint_Error;
783 return Real (Aux.Log (Double (A)) / Aux.Log (Double (Base)));
786 -------------------------
787 -- Log_Inverse_Epsilon --
788 -------------------------
790 -- Cannot precompute this constant, because this is required to be a
791 -- pure package, which allows no state. A pity, but no way around it!
793 function Log_Inverse_Epsilon return Real is
795 return Real (Aux.Log (DIEpsilon));
796 end Log_Inverse_Epsilon;
804 function Sin (A : Real) return Real is
806 if abs A < Square_Root_Epsilon then
810 return Real (Aux.Sin (Double (A)));
815 function Sin (A, Cycle : Real) return Real is
820 raise Argument_Error;
826 T := Exact_Remainder (A, Cycle) / Cycle;
828 if T = 0.0 or T = 0.5 or T = -0.5 then
831 elsif T = 0.25 or T = -0.75 then
834 elsif T = -0.25 or T = 0.75 then
839 return Real (Aux.Sin (Double (T * Two_Pi)));
846 function Sinh (A : Real) return Real is
848 if abs A < Square_Root_Epsilon then
851 elsif A > Log_Inverse_Epsilon then
852 return Exp (A - Log_Two);
854 elsif A < -Log_Inverse_Epsilon then
855 return -Exp ((-A) - Log_Two);
858 return Real (Aux.Sinh (Double (A)));
862 raise Constraint_Error;
865 -------------------------
866 -- Square_Root_Epsilon --
867 -------------------------
869 -- Cannot precompute this constant, because this is required to be a
870 -- pure package, which allows no state. A pity, but no way around it!
872 function Square_Root_Epsilon return Real is
874 return Real (Aux.Sqrt (DEpsilon));
875 end Square_Root_Epsilon;
881 function Sqrt (A : Real) return Real is
884 raise Argument_Error;
886 -- Special case Sqrt (0.0) to preserve possible minus sign per IEEE
891 -- Sqrt (1.0) must be exact for good complex accuracy
898 return Real (Aux.Sqrt (Double (A)));
907 function Tan (A : Real) return Real is
909 if abs A < Square_Root_Epsilon then
912 elsif abs A = Pi / 2.0 then
913 raise Constraint_Error;
916 return Real (Aux.tan (Double (A)));
921 function Tan (A, Cycle : Real) return Real is
926 raise Argument_Error;
932 T := Exact_Remainder (A, Cycle) / Cycle;
939 raise Constraint_Error;
942 return Sin (T * Two_Pi) / Cos (T * Two_Pi);
950 function Tanh (A : Real) return Real is
952 if A < Half_Log_Epsilon then
955 elsif A > -Half_Log_Epsilon then
958 elsif abs A < Square_Root_Epsilon then
962 return Real (Aux.tanh (Double (A)));
965 ----------------------------
966 -- DEC-Specific functions --
967 ----------------------------
969 function LOG10 (A : REAL) return REAL is
971 return Log (A, 10.0);
974 function LOG2 (A : REAL) return REAL is
979 function ASIN (A : REAL) return REAL renames Arcsin;
980 function ACOS (A : REAL) return REAL renames Arccos;
982 function ATAN (A : REAL) return REAL is
984 return Arctan (A, 1.0);
987 function ATAN2 (A1, A2 : REAL) return REAL renames Arctan;
989 function SIND (A : REAL) return REAL is
991 return Sin (A, 360.0);
994 function COSD (A : REAL) return REAL is
996 return Cos (A, 360.0);
999 function TAND (A : REAL) return REAL is
1001 return Tan (A, 360.0);
1004 function ASIND (A : REAL) return REAL is
1006 return Arcsin (A, 360.0);
1009 function ACOSD (A : REAL) return REAL is
1011 return Arccos (A, 360.0);
1014 function Arctan (A : REAL) return REAL is
1016 return Arctan (A, 1.0, 360.0);
1019 function ATAND (A : REAL) return REAL is
1021 return Arctan (A, 1.0, 360.0);
1024 function ATAN2D (A1, A2 : REAL) return REAL is
1026 return Arctan (A1, A2, 360.0);