Hi, all
While using gmp for my own work
gmp 4.1.2 for djgpp
I have found small bugs in the interface gmp.pas which cause compiling errors
when compiling for gmp 4 (it compiles fine with gmp 3)
The diff file is attached: gmp.pas.minimal.diff
While working with it I have found an other error in the provided arctan(x) function.
It works only for |x| <= 1, diverges above.
For |x|>1 the simplest way is to use
arctan(x)+arctan(1/x)=pi/2
but the convergence of the corresponding series is exceedingly slow at (near) |x|=1
I have programmed the same trick as in the QLib CEPHES library (which uses
a home made 100 digits format instead of gmp), namely
- use the previous formula to fold x above x=tan(3pi/8) = sqrt(2)+1
- use the formula
arctan(x)=pi/4 +arctan((x-1)/(x+1))
to fold x comprised between tan(pi/8)=sqrt(2)-1 and tan(3pi/8)=sqrt(2)+1
The series has to be computed only for |x|<=0.414 where the speed of convergence
is reasonable.
In the CEPHES library Moshier uses a continuous fraction development instead of the
series. I suppose this is a tradeoff speed/accuracy (not sure): the continuous
fraction has only positive instead of alternating terms, which minimises rounding errors,
but it is programmed with a fixed number of terms, of the order of the maximum
number of terms in the series in the tests I have done, forbidding to benefit of the faster
convergence for small |x|. I thus kept the series.
Notice that pi included in these formulas is computed by a double call
to arctan ! This is not a closed loop because pi needs only x=1/5 and x=1/239
below the lowest threshold. (Moshier uses a constant for pi because he has
a fixed number of digits).
I needed also the sin and cos functions.
I have programmed the usual series development for sin, after reduction
to the range [0,pi/2[, and cos(x)=sin(pi/2-x).
All this is included in gmp.pas.full.diff, which includes also the diff
contained in the minimal.diff (do not apply both).
With this we have for gmp all the basic transcendental functions required
for pascal (in the real type !)
Hope this helps
Maurice
--
Maurice Lombardi
Laboratoire de Spectrometrie Physique,
Universite Joseph Fourier de Grenoble, BP87
38402 Saint Martin d'Heres Cedex FRANCE
Tel: 33 (0)4 76 51 47 51
Fax: 33 (0)4 76 63 54 95
mailto:Maurice.Lombardi@ujf-grenoble.fr
--- gmp.pas.orig 2004-02-07 18:27:00.000000000 +0000
+++ gmp.pas 2004-04-10 16:06:24.000000000 +0000
@@ -330,7 +330,7 @@
{$endif}
{ New declarations in GMP 3.x. @@ Mostly untested! }
-{$ifdef HAVE_GMP3}
+{$if not defined (HAVE_GMP2)}
{ Available random number generation algorithms. }
type
@@ -359,8 +359,8 @@
end;
procedure gmp_randinit (var State: gmp_randstate_t; Alg: gmp_randalg_t; ...); external name '__gmp_randinit';
-procedure gmp_randinit_lc (var State: gmp_randstate_t; a: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t; c: MedCard; m: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t); external name '__gmp_randinit_lc';
-procedure gmp_randinit_lc_2exp (var State: gmp_randstate_t; a: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t; c: MedCard; M2Exp: MedCard); external name '__gmp_randinit_lc_2exp';
+procedure gmp_randinit_lc (var State: gmp_randstate_t; {$ifdef HAVE_GMP4} protected var {$endif} a: mpz_t; c: MedCard; {$ifdef HAVE_GMP4} protected var {$endif} m: mpz_t); external name '__gmp_randinit_lc';
+procedure gmp_randinit_lc_2exp (var State: gmp_randstate_t; {$ifdef HAVE_GMP4} protected var {$endif} a: mpz_t; c: MedCard; M2Exp: MedCard); external name '__gmp_randinit_lc_2exp';
procedure gmp_randseed (var State: gmp_randstate_t; Seed: mpz_t); external name '__gmp_randseed';
procedure gmp_randseed_ui (var State: gmp_randstate_t; Seed: MedCard); external name '__gmp_randseed_ui';
procedure gmp_randclear (var State: gmp_randstate_t); external name '__gmp_randclear';
--- gmp.pas.orig 2004-02-07 18:27:00.000000000 +0000
+++ gmp.pas 2004-04-10 17:17:52.000000000 +0000
@@ -330,7 +330,7 @@
{$endif}
{ New declarations in GMP 3.x. @@ Mostly untested! }
-{$ifdef HAVE_GMP3}
+{$if not defined (HAVE_GMP2)}
{ Available random number generation algorithms. }
type
@@ -359,8 +359,8 @@
end;
procedure gmp_randinit (var State: gmp_randstate_t; Alg: gmp_randalg_t; ...); external name '__gmp_randinit';
-procedure gmp_randinit_lc (var State: gmp_randstate_t; a: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t; c: MedCard; m: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t); external name '__gmp_randinit_lc';
-procedure gmp_randinit_lc_2exp (var State: gmp_randstate_t; a: {$ifdef HAVE_GMP4} protected var {$endif} mpz_t; c: MedCard; M2Exp: MedCard); external name '__gmp_randinit_lc_2exp';
+procedure gmp_randinit_lc (var State: gmp_randstate_t; {$ifdef HAVE_GMP4} protected var {$endif} a: mpz_t; c: MedCard; {$ifdef HAVE_GMP4} protected var {$endif} m: mpz_t); external name '__gmp_randinit_lc';
+procedure gmp_randinit_lc_2exp (var State: gmp_randstate_t; {$ifdef HAVE_GMP4} protected var {$endif} a: mpz_t; c: MedCard; M2Exp: MedCard); external name '__gmp_randinit_lc_2exp';
procedure gmp_randseed (var State: gmp_randstate_t; Seed: mpz_t); external name '__gmp_randseed';
procedure gmp_randseed_ui (var State: gmp_randstate_t; Seed: MedCard); external name '__gmp_randseed_ui';
procedure gmp_randclear (var State: gmp_randstate_t); external name '__gmp_randclear';
@@ -395,6 +395,8 @@
procedure mpf_ceil (var Dest: mpf_t; protected var Src: mpf_t); external name '__gmpf_ceil';
procedure mpf_floor (var Dest: mpf_t; protected var Src: mpf_t); external name '__gmpf_floor';
+function mpf_get_si (protected var Src: mpf_t): MedInt; external name '__gmpf_get_si';
+function mpf_get_ui (protected var Src: mpf_t): MedCard; external name '__gmpf_get_ui';
procedure mpf_pow_ui (var Dest: mpf_t; protected var Src1: mpf_t; Src2: MedCard); external name '__gmpf_pow_ui';
procedure mpf_trunc (var Dest: mpf_t; protected var Src: mpf_t); external name '__gmpf_trunc';
procedure mpf_urandomb (ROP: mpf_t; var State: gmp_randstate_t; n: MedCard); external name '__gmpf_urandomb';
@@ -419,6 +421,8 @@
procedure mpf_pow (var Dest: mpf_t; protected var Src1, Src2: mpf_t);
procedure mpf_arctan (var c: mpf_t; protected var x: mpf_t);
procedure mpf_pi (var c: mpf_t);
+procedure mpf_sin (var c: mpf_t; protected var x: mpf_t);
+procedure mpf_cos (var Dest: mpf_t; protected var Src: mpf_t);
implementation
@@ -588,32 +592,57 @@
end;
procedure mpf_arctan (var c: mpf_t; protected var x: mpf_t);
-{ $$\arctan x = \sum_{n=0}^{\infty} (-1)^n \frac{x^{2n+1}}{2n+1}$$ }
-var
- p, mx2, c0, s: mpf_t;
- Precision, n: MedCard;
-begin
- Precision := mpf_get_prec (c);
- mpf_init2 (p, Precision);
- mpf_set (p, x);
- mpf_init2 (mx2, Precision);
- mpf_mul (mx2, x, x);
- mpf_neg (mx2, mx2);
- mpf_init2 (c0, Precision);
- mpf_init2 (s, Precision);
- mpf_set (c, x);
- n := 1;
- repeat
- mpf_mul (p, p, mx2);
- mpf_div_ui (s, p, 2 * n + 1);
- mpf_set (c0, c);
- mpf_add (c, c, s);
- Inc (n)
- until mpf_eq (c0, c, Precision) <> 0;
- mpf_clear (s);
- mpf_clear (c0);
- mpf_clear (mx2);
- mpf_clear (p)
+{ $$\arctan x = \sum_{n=0}^{\infty} (-1)^n \frac{x^{2n+1}}{2n+1}$$
+ after double range reduction
+ around $\tan(3\pi/8)=\sqrt(2)+1$ with $\arctan x = \pi/2+\arctan(-1/x)$
+ around $\tan(\pi/8)=\sqrt(2)-1$ with $\arctan x = \pi/4+\arctan((x-1)/(x+1))$ }
+var
+ Precision, n: MedCard;
+ xx, mx2, a, b, aa, bb: mpf_t;
+begin
+ Precision := mpf_get_prec (c);
+ mpf_init2 (xx, Precision);
+ mpf_init2 (mx2, Precision);
+ mpf_init2 (a, Precision);
+ mpf_init2 (b, Precision);
+ mpf_abs (xx, x);
+ mpf_sqrt_ui (a, 2);
+ mpf_sub_ui (b, a, 1);
+ mpf_add_ui (a, a, 1);
+ if mpf_cmp (xx, a) > 0 then begin
+ mpf_pi (c);
+ mpf_div_2exp (c, c, 1);
+ mpf_ui_div (xx, 1, xx);
+ mpf_neg (xx, xx)
+ end else if mpf_cmp (xx, b) > 0 then begin
+ mpf_pi (c);
+ mpf_div_2exp (c, c, 2);
+ mpf_init2 (aa, Precision);
+ mpf_init2 (bb, Precision);
+ mpf_sub_ui (aa, xx, 1);
+ mpf_add_ui (bb, xx, 1);
+ mpf_div (xx, aa, bb);
+ mpf_clear (aa);
+ mpf_clear (bb);
+ end else
+ mpf_set_ui (c, 0);
+ mpf_mul (mx2, xx, xx);
+ mpf_neg (mx2, mx2);
+ mpf_add (c, c, xx);
+ n := 1;
+ repeat
+ mpf_mul (xx, xx, mx2);
+ mpf_div_ui (a, xx, 2*n+1);
+ mpf_set (b, c);
+ mpf_add (c, c, a);
+ Inc (n)
+ until mpf_eq (b, c, Precision) <> 0;
+ if mpf_sgn (x) < 0 then
+ mpf_neg (c, c);
+ mpf_clear (xx);
+ mpf_clear (mx2);
+ mpf_clear (a);
+ mpf_clear (b);
end;
procedure mpf_pi (var c: mpf_t);
@@ -633,4 +662,77 @@
mpf_clear (b)
end;
+procedure mpf_sin (var c: mpf_t; protected var x: mpf_t);
+{ $$\sin x = \sum_{n=0}^{\infty} (-1)^n \frac{x^{2n+1}}{(2n+1)!}$$
+ after reduction to range $[0,\pi/2[$ }
+var
+ Precision, quadrant: MedCard;
+ sign: integer;
+ a, b, z, xx, c0: mpf_t;
+begin
+ Precision := mpf_get_prec (c);
+ mpf_init2 (a, Precision);
+ mpf_init2 (b, Precision);
+ mpf_init2 (z, Precision);
+ mpf_init2 (xx, Precision);
+ mpf_init2 (c0, Precision);
+ sign := mpf_sgn(x);
+ mpf_abs (xx, x);
+ mpf_pi (z);
+ mpf_div_2exp (z, z, 1);
+ mpf_div (a, xx, z);
+ mpf_floor (xx, a);
+ if mpf_cmp_ui (xx, 4) >= 0 then begin
+ mpf_div_2exp (b, xx, 2);
+ mpf_floor (b, b);
+ mpf_mul_2exp (b, b, 2);
+ end else
+ mpf_set_ui (b, 0);
+ mpf_sub (b, xx, b);
+{$if not defined (HAVE_GMP2)}
+ quadrant := mpf_get_ui (b);
+{$else}
+ quadrant := round (mpf_get_d (b));
+{$endif}
+ mpf_sub (b, a, xx);
+ mpf_mul (xx, z, b);
+ if quadrant > 1 then
+ sign := -sign;
+ if (quadrant and 1) <> 0 then
+ mpf_sub (xx, z, xx);
+ mpf_mul (z, xx, xx);
+ mpf_neg (z, z);
+ mpf_set_ui (a, 1);
+ mpf_set_ui (b, 1);
+ mpf_set_ui (c, 1);
+ repeat
+ mpf_add_ui (a, a, 1);
+ mpf_div (b, b, a);
+ mpf_add_ui (a, a, 1);
+ mpf_div (b, b, a);
+ mpf_mul (b, b, z);
+ mpf_set (c0, c);
+ mpf_add (c, c, b);
+ until mpf_eq (c0, c, Precision) <> 0;
+ mpf_mul (c, c, xx);
+ if sign < 0 then
+ mpf_neg (c, c);
+ mpf_clear (a);
+ mpf_clear (b);
+ mpf_clear (z);
+ mpf_clear (xx);
+ mpf_clear (c0);
+end;
+
+procedure mpf_cos (var Dest: mpf_t; protected var Src: mpf_t);
+var Temp: mpf_t;
+begin
+ mpf_init2 (Temp, mpf_get_prec (Dest));
+ mpf_pi (Temp);
+ mpf_div_2exp (Temp, Temp, 1);
+ mpf_sub (Temp, Temp, Src);
+ mpf_sin (Dest, Temp);
+ mpf_clear (Temp);
+end;
+
end.