xref: /netbsd-src/external/lgpl3/mpc/dist/src/balls.c (revision 367b82799ab709709d3c3b541df56a2a14644d3e)
1 /* balls -- Functions for complex ball arithmetic.
2 
3 Copyright (C) 2018, 2020, 2021, 2022 INRIA
4 
5 This file is part of GNU MPC.
6 
7 GNU MPC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15 more details.
16 
17 You should have received a copy of the GNU Lesser General Public License
18 along with this program. If not, see http://www.gnu.org/licenses/ .
19 */
20 
21 #include <limits.h> /* for CHAR_BIT */
22 #include <stdio.h>  /* for FILE */
23 #include "mpc-impl.h"
24 
mpcb_out_str(FILE * f,mpcb_srcptr op)25 void mpcb_out_str (FILE *f, mpcb_srcptr op)
26 {
27    mpc_out_str (f, 10, 0, op->c, MPC_RNDNN);
28    fprintf (f, " ");
29    mpcr_out_str (f, op->r);
30    fprintf (f, "\n");
31 }
32 
33 void
mpcb_init(mpcb_ptr rop)34 mpcb_init (mpcb_ptr rop)
35 {
36    mpc_init2 (rop->c, 2);
37    mpcr_set_inf (rop->r);
38 }
39 
40 
41 void
mpcb_clear(mpcb_ptr rop)42 mpcb_clear (mpcb_ptr rop)
43 {
44    mpc_clear (rop->c);
45 }
46 
47 
48 mpfr_prec_t
mpcb_get_prec(mpcb_srcptr op)49 mpcb_get_prec (mpcb_srcptr op)
50 {
51    return mpc_get_prec (op->c);
52 }
53 
54 
55 void
mpcb_set_prec(mpcb_ptr rop,mpfr_prec_t prec)56 mpcb_set_prec (mpcb_ptr rop, mpfr_prec_t prec)
57 {
58    mpc_set_prec (rop->c, prec);
59    mpcr_set_inf (rop->r);
60 }
61 
62 
63 void
mpcb_set(mpcb_ptr rop,mpcb_srcptr op)64 mpcb_set (mpcb_ptr rop, mpcb_srcptr op)
65 {
66    if (rop != op) {
67       mpc_set_prec (rop->c, mpc_get_prec (op->c));
68       mpc_set (rop->c, op->c, MPC_RNDNN);
69       mpcr_set (rop->r, op->r);
70    }
71 }
72 
73 
74 void
mpcb_set_inf(mpcb_ptr rop)75 mpcb_set_inf (mpcb_ptr rop)
76 {
77    mpc_set_prec (rop->c, 2);
78    mpc_set_ui_ui (rop->c, 0, 0, MPC_RNDNN);
79    mpcr_set_inf (rop->r);
80 }
81 
82 
83 void
mpcb_set_c(mpcb_ptr rop,mpc_srcptr op,mpfr_prec_t prec,unsigned long int err_re,unsigned long int err_im)84 mpcb_set_c (mpcb_ptr rop, mpc_srcptr op, mpfr_prec_t prec,
85    unsigned long int err_re, unsigned long int err_im)
86    /* Set the precision of rop to prec and assign a ball with centre op
87       to it. err_re and err_im contain potential errors in the real and
88       imaginary parts of op as multiples of a half ulp. For instance,
89       if the real part of op is exact, err_re should be set to 0;
90       if it is the result of rounding to nearest, it should be set to 1;
91       if it is the result of directed rounding, it should be set to 2.
92       The radius of the ball reflects err_re and err_im and the potential
93       additional rounding error that can occur when the precision of op
94       is higher than prec. If the real part of op is 0, then err_re
95       should be 0, since then ulp notation makes no sense, and similarly
96       for the imaginary part; otherwise the radius is set to infinity.
97       The implementation takes potential different precisions in the real
98       and imaginary parts of op into account. */
99 {
100    int inex;
101    mpcr_t relerr_re, relerr_im;
102 
103    mpc_set_prec (rop->c, prec);
104    inex = mpc_set (rop->c, op, MPC_RNDNN);
105 
106    if (   (mpfr_zero_p (mpc_realref (op)) && err_re > 0)
107        || (mpfr_zero_p (mpc_imagref (op)) && err_im > 0)
108        || !mpc_fin_p (op))
109        mpcr_set_inf (rop->r);
110    else {
111       mpcr_set_ui64_2si64 (relerr_re, (uint64_t) err_re,
112          (int64_t) (-mpfr_get_prec (mpc_realref (op))));
113          /* prop:relerror of algorithms.tex */
114       if (MPC_INEX_RE (inex))
115          mpcr_add_rounding_error (relerr_re, prec, MPFR_RNDN);
116       mpcr_set_ui64_2si64 (relerr_im, (uint64_t) err_im,
117          (int64_t) (-mpfr_get_prec (mpc_imagref (op))));
118       if (MPC_INEX_IM (inex))
119          mpcr_add_rounding_error (relerr_im, prec, MPFR_RNDN);
120       mpcr_max (rop->r, relerr_re, relerr_im);
121          /* prop:comrelerror in algorithms.tex */
122    }
123 }
124 
125 
126 void
mpcb_set_ui_ui(mpcb_ptr z,unsigned long int re,unsigned long int im,mpfr_prec_t prec)127 mpcb_set_ui_ui (mpcb_ptr z, unsigned long int re, unsigned long int im,
128    mpfr_prec_t prec)
129    /* Set the precision of z to prec and assign a ball with centre
130       re+I*im to it. If prec is too small to hold the centre coordinates
131       without rounding, use the minimal possible precision instead. */
132 {
133    prec = MPC_MAX (prec,
134       (mpfr_prec_t) (CHAR_BIT * sizeof (unsigned long int)));
135    mpc_set_prec (z->c, prec);
136    mpc_set_ui_ui (z->c, re, im, MPC_RNDNN);
137    mpcr_set_zero (z->r);
138 }
139 
140 
141 void
mpcb_neg(mpcb_ptr z,mpcb_srcptr z1)142 mpcb_neg (mpcb_ptr z, mpcb_srcptr z1)
143 {
144    mpfr_prec_t p;
145    int overlap = (z == z1);
146 
147    if (!overlap) {
148       p = mpcb_get_prec (z1);
149       if (mpcb_get_prec (z) != p)
150          mpcb_set_prec (z, p);
151    }
152 
153    mpc_neg (z->c, z1->c, MPC_RNDNN); /* exact */
154    mpcr_set (z->r, z1->r);
155 }
156 
157 
158 void
mpcb_mul(mpcb_ptr z,mpcb_srcptr z1,mpcb_srcptr z2)159 mpcb_mul (mpcb_ptr z, mpcb_srcptr z1, mpcb_srcptr z2)
160 {
161    mpcr_t r;
162    mpfr_prec_t p = MPC_MIN (mpcb_get_prec (z1), mpcb_get_prec (z2));
163    int overlap = (z == z1 || z == z2);
164    mpc_t zc;
165 
166    if (overlap)
167       mpc_init2 (zc, p);
168    else {
169       zc [0] = z->c [0];
170       mpc_set_prec (zc, p);
171    }
172    mpc_mul (zc, z1->c, z2->c, MPC_RNDNN);
173    if (overlap)
174       mpc_clear (z->c);
175    z->c [0] = zc [0];
176 
177    /* generic error of multiplication */
178    mpcr_mul (r, z1->r, z2->r);
179    mpcr_add (r, r, z1->r);
180    mpcr_add (r, r, z2->r);
181    /* error of rounding to nearest */
182    mpcr_add_rounding_error (r, p, MPFR_RNDN);
183    mpcr_set (z->r, r);
184 }
185 
186 
187 void
mpcb_sqr(mpcb_ptr z,mpcb_srcptr z1)188 mpcb_sqr (mpcb_ptr z, mpcb_srcptr z1)
189 {
190    mpcr_t r, r2;
191    mpfr_prec_t p = mpcb_get_prec (z1);
192    int overlap = (z == z1);
193 
194    /* Compute the error first in case there is overlap. */
195    mpcr_mul_2ui (r2, z1->r, 1);
196    mpcr_sqr (r, z1->r);
197    mpcr_add (r, r, r2);
198    mpcr_add_rounding_error (r, p, MPFR_RNDN);
199 
200    if (!overlap)
201       mpcb_set_prec (z, p);
202    mpc_sqr (z->c, z1->c, MPC_RNDNN);
203    mpcr_set (z->r, r);
204 }
205 
206 void
mpcb_pow_ui(mpcb_ptr z,mpcb_srcptr z1,unsigned long int e)207 mpcb_pow_ui (mpcb_ptr z, mpcb_srcptr z1, unsigned long int e)
208 {
209    mpcb_t pow;
210 
211    if (e == 0)
212       mpcb_set_ui_ui (z, 1, 0, mpcb_get_prec (z1));
213    else if (e == 1)
214      mpcb_set (z, z1);
215    else {
216       /* Right to left powering is easier to implement, but requires an
217          additional variable even when there is no overlap. */
218       mpcb_init (pow);
219       mpcb_set (pow, z1);
220       /* Avoid setting z to 1 and multiplying by it, instead set it to the
221          smallest 2-power multiple of z1 that is occurring. */
222       while (e % 2 == 0) {
223          mpcb_sqr (pow, pow);
224          e /= 2;
225       }
226       mpcb_set (z, pow);
227       e /= 2;
228       while (e != 0) {
229          mpcb_sqr (pow, pow);
230          if (e % 2 == 1)
231             mpcb_mul (z, z, pow);
232          e /= 2;
233       }
234       mpcb_clear (pow);
235    }
236 }
237 
238 
239 void
mpcb_add(mpcb_ptr z,mpcb_srcptr z1,mpcb_srcptr z2)240 mpcb_add (mpcb_ptr z, mpcb_srcptr z1, mpcb_srcptr z2)
241 {
242    mpcr_t r, s, denom;
243    mpfr_prec_t p = MPC_MIN (mpcb_get_prec (z1), mpcb_get_prec (z2));
244    int overlap = (z == z1 || z == z2);
245    mpc_t zc;
246 
247    if (overlap)
248       mpc_init2 (zc, p);
249    else {
250       zc [0] = z->c [0];
251       mpc_set_prec (zc, p);
252    }
253    mpc_add (zc, z1->c, z2->c, MPC_RNDZZ);
254       /* rounding towards 0 makes the generic error easier to compute,
255          but incurs a tiny penalty for the rounding error */
256 
257    /* generic error of addition:
258       r <= (|z1|*r1 + |z2|*r2) / |z1+z2|
259         <= (|z1|*r1 + |z2|*r2) / |z| since we rounded towards 0 */
260    mpcr_c_abs_rnd (denom, zc, MPFR_RNDD);
261    mpcr_c_abs_rnd (r, z1->c, MPFR_RNDU);
262    mpcr_mul (r, r, z1->r);
263    mpcr_c_abs_rnd (s, z2->c, MPFR_RNDU);
264    mpcr_mul (s, s, z2->r);
265    mpcr_add (r, r, s);
266    mpcr_div (r, r, denom);
267    /* error of directed rounding */
268    mpcr_add_rounding_error (r, p, MPFR_RNDZ);
269 
270    if (overlap)
271       mpc_clear (z->c);
272    z->c [0] = zc [0];
273    mpcr_set (z->r, r);
274 }
275 
276 
277 void
mpcb_sqrt(mpcb_ptr z,mpcb_srcptr z1)278 mpcb_sqrt (mpcb_ptr z, mpcb_srcptr z1)
279    /* The function "glides over" the branch cut on the negative real axis:
280       In fact it always returns a ball with centre the square root of the
281       centre of z1, and a reasonable radius even when the input ball has a
282       crosses the negative real axis. This is inconsistent in a sense: The
283       output ball does not contain all the possible outputs of a call to
284       mpc_sqrt on an element of the input ball. On the other hand, it does
285       contain square roots of all elements of the input ball. This comes
286       handy for the alternative implementation of mpc_agm using ball
287       arithmetic, but would also cause a potential implementation of
288       mpcb_agm to ignore the branch cut. */
289 {
290    mpcr_t r;
291    mpfr_prec_t p = mpcb_get_prec (z1);
292    int overlap = (z == z1);
293 
294    /* Compute the error first in case there is overlap. */
295    /* generic error of square root for z1->r <= 0.5:
296       0.5*epsilon1 + (sqrt(2)-1) * epsilon1^2
297       <= 0.5 * epsilon1 * (1 + epsilon1),
298       see eq:propsqrt in algorithms.tex, together with a Taylor
299       expansion of 1/sqrt(1-epsilon1) */
300    if (!mpcr_lt_half_p (z1->r))
301       mpcr_set_inf (r);
302    else {
303       mpcr_set_one (r);
304       mpcr_add (r, r, z1->r);
305       mpcr_mul (r, r, z1->r);
306       mpcr_div_2ui (r, r, 1);
307       /* error of rounding to nearest */
308       mpcr_add_rounding_error (r, p, MPFR_RNDN);
309    }
310 
311    if (!overlap)
312       mpcb_set_prec (z, p);
313    mpc_sqrt (z->c, z1->c, MPC_RNDNN);
314    mpcr_set (z->r, r);
315 }
316 
317 
318 void
mpcb_div(mpcb_ptr z,mpcb_srcptr z1,mpcb_srcptr z2)319 mpcb_div (mpcb_ptr z, mpcb_srcptr z1, mpcb_srcptr z2)
320 {
321    mpcr_t r, s;
322    mpfr_prec_t p = MPC_MIN (mpcb_get_prec (z1), mpcb_get_prec (z2));
323    int overlap = (z == z1 || z == z2);
324    mpc_t zc;
325 
326    if (overlap)
327       mpc_init2 (zc, p);
328    else {
329       zc [0] = z->c [0];
330       mpc_set_prec (zc, p);
331    }
332    mpc_div (zc, z1->c, z2->c, MPC_RNDNN);
333    if (overlap)
334       mpc_clear (z->c);
335    z->c [0] = zc [0];
336 
337    /* generic error of division */
338    mpcr_add (r, z1->r, z2->r);
339    mpcr_set_one (s);
340    mpcr_sub_rnd (s, s, z2->r, MPFR_RNDD);
341    mpcr_div (r, r, s);
342    /* error of rounding to nearest */
343    mpcr_add_rounding_error (r, p, MPFR_RNDN);
344    mpcr_set (z->r, r);
345 }
346 
347 
348 void
mpcb_div_2ui(mpcb_ptr z,mpcb_srcptr z1,unsigned long int e)349 mpcb_div_2ui (mpcb_ptr z, mpcb_srcptr z1, unsigned long int e)
350 {
351    mpc_div_2ui (z->c, z1->c, e, MPC_RNDNN);
352    mpcr_set (z->r, z1->r);
353 }
354 
355 
356 int
mpcb_can_round(mpcb_srcptr op,mpfr_prec_t prec_re,mpfr_prec_t prec_im,mpc_rnd_t rnd)357 mpcb_can_round (mpcb_srcptr op, mpfr_prec_t prec_re, mpfr_prec_t prec_im,
358    mpc_rnd_t rnd)
359    /* The function returns true if it can decide that rounding the centre
360       of op to an mpc_t variable of precision prec_re for the real and
361       prec_im for the imaginary part returns a correctly rounded result
362       of the ball in direction rnd for which the rounding direction value
363       can be determined. The second condition implies that if the centre
364       can be represented at the target precisions and the radius is small,
365       but non-zero, the function returns false although correct rounding
366       would be possible, while the rounding direction value could be
367       anything.
368       If the return value is true, then using mpcb_round with the same
369       rounding mode sets a correct result and returns a correct rounding
370       direction value with the usual MPC semantic. */
371 {
372    mpfr_srcptr re, im;
373    mpfr_exp_t exp_re, exp_im, exp_err;
374 
375    if (mpcr_inf_p (op->r))
376       return 0;
377    else if (mpcr_zero_p (op->r))
378       return 1;
379 
380    re = mpc_realref (op->c);
381    im = mpc_imagref (op->c);
382    /* If the real or the imaginary part of the centre is 0, directed
383       rounding is impossible, and rounding to nearest is only possible
384       if the absolute error is less than the smallest representable
385       number; since rounding only once at precision p introduces an error
386       of about 2^-p, this means that the precision needs to be about as
387       big as the negative of the minimal exponent, which is astronomically
388       large. In any case, even then we could not determine the rounding
389       direction value. */
390    if (mpfr_zero_p (re) || mpfr_zero_p (im))
391       return 0;
392 
393    exp_re = mpfr_get_exp (re);
394    exp_im = mpfr_get_exp (im);
395 
396    /* Absolute error of the real part, as given in the proof of
397       prop:comrelerror of algorithms.tex:
398       |x-x~|  = |x~*theta_R - y~*theta_I|
399              <= (|x~|+|y~|) * epsilon,
400                 where epsilon is the complex relative error
401              <= 2 * max (|x~|, |y~|) * epsilon
402       To call mpfr_can_round, we only need the exponent in base 2,
403       which is then bounded above by
404                 1 + max (exp_re, exp_im) + exponent (epsilon) */
405    exp_err = 1 + MPC_MAX (exp_re, exp_im) + mpcr_get_exp (op->r);
406 
407    /* To check whether the rounding direction value can be determined
408       when rounding to nearest, use the trick described in the
409       documentation of mpfr_can_round to check for directed rounding
410       at precision larger by 1. */
411    return (   mpfr_can_round (re, exp_re - exp_err, MPFR_RNDN,
412                  MPFR_RNDZ, prec_re + (MPC_RND_RE (rnd) == MPFR_RNDN))
413            && mpfr_can_round (im, exp_im - exp_err, MPFR_RNDN,
414                  MPFR_RNDZ, prec_im + (MPC_RND_IM (rnd) == MPFR_RNDN)));
415 }
416 
417 
418 int
mpcb_round(mpc_ptr rop,mpcb_srcptr op,mpc_rnd_t rnd)419 mpcb_round (mpc_ptr rop, mpcb_srcptr op, mpc_rnd_t rnd)
420    /* Set rop to the centre of op and return the corresponding rounding
421       direction value. To make sure that this corresponds to the MPC
422       semantics of returning a correctly rounded result and a correct
423       rounding direction value, one needs to call mpcb_can_round first. */
424 {
425    return mpc_set (rop, op->c, rnd);
426 }
427 
428