1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
9 -- Copyright (C) 1992-2009, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- This body is specifically for using an Ada interface to C math.h to get
33 -- the computation engine. Many special cases are handled locally to avoid
34 -- unnecessary calls. This is not a "strict" implementation, but takes full
35 -- advantage of the C functions, e.g. in providing interface to hardware
36 -- provided versions of the elementary functions.
38 -- A known weakness is that on the x86, all computation is done in Double,
39 -- which means that a lot of accuracy is lost for the Long_Long_Float case.
41 -- Uses functions sqrt, exp, log, pow, sin, asin, cos, acos, tan, atan,
42 -- sinh, cosh, tanh from C library via math.h
44 -- This is an adaptation of Ada.Numerics.Generic_Elementary_Functions that
45 -- provides a compatible body for the DEC Math_Lib package.
47 with Ada
.Numerics
.Aux
;
48 use type Ada
.Numerics
.Aux
.Double
;
49 with Ada
.Numerics
; use Ada
.Numerics
;
51 package body Math_Lib
is
53 Log_Two
: constant := 0.69314_71805_59945_30941_72321_21458_17656_80755
;
55 Two_Pi
: constant Real
'Base := 2.0 * Pi
;
56 Half_Pi
: constant Real
'Base := Pi
/ 2.0;
57 Fourth_Pi
: constant Real
'Base := Pi
/ 4.0;
58 Epsilon
: constant Real
'Base := Real
'Base'Epsilon;
59 IEpsilon : constant Real'Base := 1.0 / Epsilon;
61 subtype Double is Aux.Double;
63 DEpsilon : constant Double := Double (Epsilon);
64 DIEpsilon : constant Double := Double (IEpsilon);
66 -----------------------
67 -- Local Subprograms --
68 -----------------------
81 function Exact_Remainder
85 -- Computes exact remainder of A divided by Y
87 function Half_Log_Epsilon return Real;
88 -- Function to provide constant: 0.5 * Log (Epsilon)
94 -- Common code for arc tangent after cycle reduction
96 function Log_Inverse_Epsilon return Real;
97 -- Function to provide constant: Log (1.0 / Epsilon)
99 function Square_Root_Epsilon return Real;
100 -- Function to provide constant: Sqrt (Epsilon)
106 function "**" (A1, A2 : Real) return Real is
112 raise Argument_Error;
115 raise Argument_Error;
122 raise Constraint_Error;
139 Real (Aux.pow (Double (A1), Double (A2)));
144 raise Constraint_Error;
155 function Arccos (A : Real) return Real is
160 raise Argument_Error;
162 elsif abs A < Square_Root_Epsilon then
172 Temp := Real (Aux.acos (Double (A)));
183 function Arccos (A, Cycle : Real) return Real is
188 raise Argument_Error;
190 elsif abs A > 1.0 then
191 raise Argument_Error;
193 elsif abs A < Square_Root_Epsilon then
203 Temp := Arctan (Sqrt (1.0 - A * A) / A, 1.0, Cycle);
206 Temp := Cycle / 2.0 + Temp;
216 function Arccosh (A : Real) return Real is
218 -- Return Log (A - Sqrt (A * A - 1.0)); double valued,
219 -- only positive value returned
220 -- What is this comment ???
223 raise Argument_Error;
225 elsif A < 1.0 + Square_Root_Epsilon then
228 elsif abs A > 1.0 / Square_Root_Epsilon then
229 return Log (A) + Log_Two;
232 return Log (A + Sqrt (A * A - 1.0));
248 -- Just reverse arguments
250 return Arctan (Y, A);
262 -- Just reverse arguments
264 return Arctan (Y, A, Cycle);
271 function Arccoth (A : Real) return Real is
274 raise Constraint_Error;
276 elsif abs A < 1.0 then
277 raise Argument_Error;
279 elsif abs A > 1.0 / Epsilon then
283 return 0.5 * Log ((1.0 + A) / (A - 1.0));
293 function Arcsin (A : Real) return Real is
296 raise Argument_Error;
298 elsif abs A < Square_Root_Epsilon then
308 return Real (Aux.asin (Double (A)));
313 function Arcsin (A, Cycle : Real) return Real is
316 raise Argument_Error;
318 elsif abs A > 1.0 then
319 raise Argument_Error;
331 return Arctan (A / Sqrt (1.0 - A * A), 1.0, Cycle);
338 function Arcsinh (A : Real) return Real is
340 if abs A < Square_Root_Epsilon then
343 elsif A > 1.0 / Square_Root_Epsilon then
344 return Log (A) + Log_Two;
346 elsif A < -1.0 / Square_Root_Epsilon then
347 return -(Log (-A) + Log_Two);
350 return -Log (abs A + Sqrt (A * A + 1.0));
353 return Log (A + Sqrt (A * A + 1.0));
372 raise Argument_Error;
389 return Local_Atan (Y, A);
403 raise Argument_Error;
408 raise Argument_Error;
425 return Local_Atan (Y, A) * Cycle / Two_Pi;
433 function Arctanh (A : Real) return Real is
436 raise Constraint_Error;
438 elsif abs A > 1.0 then
439 raise Argument_Error;
441 elsif abs A < Square_Root_Epsilon then
445 return 0.5 * Log ((1.0 + A) / (1.0 - A));
455 function Cos (A : Real) return Real is
460 elsif abs A < Square_Root_Epsilon then
465 return Real (Aux.Cos (Double (A)));
470 function Cos (A, Cycle : Real) return Real is
475 raise Argument_Error;
481 T := Exact_Remainder (abs (A), Cycle) / Cycle;
490 elsif T = 0.5 or T = -0.5 then
494 return Real (Aux.Cos (Double (T * Two_Pi)));
501 function Cosh (A : Real) return Real is
503 if abs A < Square_Root_Epsilon then
506 elsif abs A > Log_Inverse_Epsilon then
507 return Exp ((abs A) - Log_Two);
510 return Real (Aux.cosh (Double (A)));
514 raise Constraint_Error;
523 function Cot (A : Real) return Real is
526 raise Constraint_Error;
528 elsif abs A < Square_Root_Epsilon then
532 return Real (1.0 / Real'Base (Aux.tan (Double (A))));
537 function Cot (A, Cycle : Real) return Real is
542 raise Argument_Error;
545 raise Constraint_Error;
547 elsif abs A < Square_Root_Epsilon then
551 T := Exact_Remainder (A, Cycle) / Cycle;
553 if T = 0.0 or T = 0.5 or T = -0.5 then
554 raise Constraint_Error;
556 return Cos (T * Two_Pi) / Sin (T * Two_Pi);
564 function Coth (A : Real) return Real is
567 raise Constraint_Error;
569 elsif A < Half_Log_Epsilon then
572 elsif A > -Half_Log_Epsilon then
575 elsif abs A < Square_Root_Epsilon then
579 return Real (1.0 / Real'Base (Aux.tanh (Double (A))));
582 ---------------------
583 -- Exact_Remainder --
584 ---------------------
586 function Exact_Remainder
591 Denominator : Real'Base := abs A;
592 Divisor : Real'Base := abs Y;
594 Sign : Real'Base := 1.0;
598 raise Constraint_Error;
606 elsif Denominator < Divisor then
610 while Denominator >= Divisor loop
612 -- Put divisors mantissa with denominators exponent to make reducer
617 while Reducer * 1_048_576.0 < Denominator loop
618 Reducer := Reducer * 1_048_576.0;
626 while Reducer * 1_024.0 < Denominator loop
627 Reducer := Reducer * 1_024.0;
635 while Reducer * 2.0 < Denominator loop
636 Reducer := Reducer * 2.0;
643 Denominator := Denominator - Reducer;
657 function Exp (A : Real) return Real is
665 Result := Real (Aux.Exp (Double (A)));
667 -- The check here catches the case of Exp returning IEEE infinity
669 if Result > Real'Last then
670 raise Constraint_Error;
677 ----------------------
678 -- Half_Log_Epsilon --
679 ----------------------
681 -- Cannot precompute this constant, because this is required to be a
682 -- pure package, which allows no state. A pity, but no way around it!
684 function Half_Log_Epsilon return Real is
686 return Real (0.5 * Real'Base (Aux.Log (DEpsilon)));
687 end Half_Log_Epsilon;
699 Raw_Atan : Real'Base;
702 if abs Y > abs A then
708 if Z < Square_Root_Epsilon then
712 Raw_Atan := Pi / 4.0;
714 elsif Z < Square_Root_Epsilon then
718 Raw_Atan := Real'Base (Aux.Atan (Double (Z)));
721 if abs Y > abs A then
722 Raw_Atan := Half_Pi - Raw_Atan;
734 return Pi - Raw_Atan;
736 return -(Pi - Raw_Atan);
747 function Log (A : Real) return Real is
750 raise Argument_Error;
753 raise Constraint_Error;
759 return Real (Aux.Log (Double (A)));
764 function Log (A, Base : Real) return Real is
767 raise Argument_Error;
769 elsif Base <= 0.0 or else Base = 1.0 then
770 raise Argument_Error;
773 raise Constraint_Error;
779 return Real (Aux.Log (Double (A)) / Aux.Log (Double (Base)));
782 -------------------------
783 -- Log_Inverse_Epsilon --
784 -------------------------
786 -- Cannot precompute this constant, because this is required to be a
787 -- pure package, which allows no state. A pity, but no way around it!
789 function Log_Inverse_Epsilon return Real is
791 return Real (Aux.Log (DIEpsilon));
792 end Log_Inverse_Epsilon;
800 function Sin (A : Real) return Real is
802 if abs A < Square_Root_Epsilon then
806 return Real (Aux.Sin (Double (A)));
811 function Sin (A, Cycle : Real) return Real is
816 raise Argument_Error;
822 T := Exact_Remainder (A, Cycle) / Cycle;
824 if T = 0.0 or T = 0.5 or T = -0.5 then
827 elsif T = 0.25 or T = -0.75 then
830 elsif T = -0.25 or T = 0.75 then
835 return Real (Aux.Sin (Double (T * Two_Pi)));
842 function Sinh (A : Real) return Real is
844 if abs A < Square_Root_Epsilon then
847 elsif A > Log_Inverse_Epsilon then
848 return Exp (A - Log_Two);
850 elsif A < -Log_Inverse_Epsilon then
851 return -Exp ((-A) - Log_Two);
854 return Real (Aux.Sinh (Double (A)));
858 raise Constraint_Error;
861 -------------------------
862 -- Square_Root_Epsilon --
863 -------------------------
865 -- Cannot precompute this constant, because this is required to be a
866 -- pure package, which allows no state. A pity, but no way around it!
868 function Square_Root_Epsilon return Real is
870 return Real (Aux.Sqrt (DEpsilon));
871 end Square_Root_Epsilon;
877 function Sqrt (A : Real) return Real is
880 raise Argument_Error;
882 -- Special case Sqrt (0.0) to preserve possible minus sign per IEEE
887 -- Sqrt (1.0) must be exact for good complex accuracy
894 return Real (Aux.Sqrt (Double (A)));
903 function Tan (A : Real) return Real is
905 if abs A < Square_Root_Epsilon then
908 elsif abs A = Pi / 2.0 then
909 raise Constraint_Error;
912 return Real (Aux.tan (Double (A)));
917 function Tan (A, Cycle : Real) return Real is
922 raise Argument_Error;
928 T := Exact_Remainder (A, Cycle) / Cycle;
935 raise Constraint_Error;
938 return Sin (T * Two_Pi) / Cos (T * Two_Pi);
946 function Tanh (A : Real) return Real is
948 if A < Half_Log_Epsilon then
951 elsif A > -Half_Log_Epsilon then
954 elsif abs A < Square_Root_Epsilon then
958 return Real (Aux.tanh (Double (A)));
961 ----------------------------
962 -- DEC-Specific functions --
963 ----------------------------
965 function LOG10 (A : REAL) return REAL is
967 return Log (A, 10.0);
970 function LOG2 (A : REAL) return REAL is
975 function ASIN (A : REAL) return REAL renames Arcsin;
976 function ACOS (A : REAL) return REAL renames Arccos;
978 function ATAN (A : REAL) return REAL is
980 return Arctan (A, 1.0);
983 function ATAN2 (A1, A2 : REAL) return REAL renames Arctan;
985 function SIND (A : REAL) return REAL is
987 return Sin (A, 360.0);
990 function COSD (A : REAL) return REAL is
992 return Cos (A, 360.0);
995 function TAND (A : REAL) return REAL is
997 return Tan (A, 360.0);
1000 function ASIND (A : REAL) return REAL is
1002 return Arcsin (A, 360.0);
1005 function ACOSD (A : REAL) return REAL is
1007 return Arccos (A, 360.0);
1010 function Arctan (A : REAL) return REAL is
1012 return Arctan (A, 1.0, 360.0);
1015 function ATAND (A : REAL) return REAL is
1017 return Arctan (A, 1.0, 360.0);
1020 function ATAN2D (A1, A2 : REAL) return REAL is
1022 return Arctan (A1, A2, 360.0);