xref: /netbsd-src/external/lgpl3/mpfr/dist/src/rndna.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1 /* mpfr_round_nearest_away -- round to nearest away
2 
3 Copyright 2012-2023 Free Software Foundation, Inc.
4 Contributed by the AriC and Caramba projects, INRIA.
5 
6 This file is part of the GNU MPFR Library.
7 
8 The GNU MPFR Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 The GNU MPFR Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16 License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22 
23 #include "mpfr-impl.h"
24 
25 /* Note: this doesn't work for 2^(emin-2). Currently, the best that can be
26    done is to extend the exponent range internally, and do not support the
27    case emin = MPFR_EMIN_MIN from the caller. */
28 
29 /* In order to work, we'll save the context within the mantissa of 'rop'.
30    For that, we need to do some low level stuff like for init2/clear to create
31    a mantissa of bigger size, which contains the extra context.
32    To add a new field of type T in the context, add its type in
33    mpfr_size_limb_extended_t (if it is not already present)
34    and add a new value for the enum mpfr_index_extended_t before MANTISSA. */
35 typedef union {
36   mp_size_t    si;
37   mp_limb_t    li;
38   mpfr_exp_t   ex;
39   mpfr_prec_t  pr;
40   mpfr_sign_t  sg;
41   mpfr_flags_t fl;
42   mpfr_limb_ptr pi;
43 } mpfr_size_limb_extended_t;
44 typedef enum {
45   ALLOC_SIZE = 0,
46   OLD_MANTISSA,
47   OLD_EXPONENT,
48   OLD_SIGN,
49   OLD_PREC,
50   OLD_FLAGS,
51   OLD_EXP_MIN,
52   OLD_EXP_MAX,
53   MANTISSA
54 } mpfr_index_extended_t ;
55 
56 #define MPFR_MALLOC_EXTENDED_SIZE(s) \
57   (MANTISSA * sizeof(mpfr_size_limb_extended_t) + \
58    MPFR_BYTES_PER_MP_LIMB * (size_t) (s))
59 
60 /* This function is called before the applied function
61    and prepares rop to give it one more bit of precision
62    and to save its old value within it. */
63 void
mpfr_round_nearest_away_begin(mpfr_ptr rop)64 mpfr_round_nearest_away_begin (mpfr_ptr rop)
65 {
66   mpfr_t tmp;
67   mp_size_t xsize;
68   mpfr_size_limb_extended_t *ext;
69   mpfr_prec_t p;
70   int inexact;
71   MPFR_SAVE_EXPO_DECL (expo);
72 
73   /* when emin is the smallest possible value, we cannot
74      determine the correct round-to-nearest-away rounding for
75      0.25*2^emin, which gets rounded to 0 with nearest-even,
76      instead of 0.5^2^emin. */
77   MPFR_ASSERTN(__gmpfr_emin > MPFR_EMIN_MIN);
78 
79   p = MPFR_PREC (rop) + 1;
80   MPFR_SAVE_EXPO_MARK (expo);
81 
82   /* We can't use mpfr_init2 since we need to store an additional context
83      within the mantissa. */
84   MPFR_ASSERTN(p <= MPFR_PREC_MAX);
85   /* Allocate the context within the needed mantissa. */
86   xsize = MPFR_PREC2LIMBS (p);
87   ext   = (mpfr_size_limb_extended_t *)
88     mpfr_allocate_func (MPFR_MALLOC_EXTENDED_SIZE(xsize));
89 
90   /* Save the context first. */
91   ext[ALLOC_SIZE].si   = xsize;
92   ext[OLD_MANTISSA].pi = MPFR_MANT(rop);
93   ext[OLD_EXPONENT].ex = MPFR_EXP(rop);
94   ext[OLD_SIGN].sg     = MPFR_SIGN(rop);
95   ext[OLD_PREC].pr     = MPFR_PREC(rop);
96   ext[OLD_FLAGS].fl    = expo.saved_flags;
97   ext[OLD_EXP_MIN].ex  = expo.saved_emin;
98   ext[OLD_EXP_MAX].ex  = expo.saved_emax;
99 
100   /* Create tmp as a proper NAN. */
101   MPFR_PREC(tmp) = p;                           /* Set prec */
102   MPFR_SET_POS(tmp);                            /* Set a sign */
103   MPFR_MANT(tmp) =  (mp_limb_t*)(ext+MANTISSA); /* Set Mantissa ptr */
104   MPFR_SET_NAN(tmp);                            /* initializes to NaN */
105 
106   /* Note: This full initialization to NaN may be unnecessary because of
107      the mpfr_set just below, but this is cleaner in case internals would
108      change in the future (for instance, some fields of tmp could be read
109      before being set, and reading an uninitialized value is UB here). */
110 
111   /* Copy rop into tmp now (rop may be used as input later). */
112   MPFR_DBGRES (inexact = mpfr_set(tmp, rop, MPFR_RNDN));
113   MPFR_ASSERTD(inexact == 0); /* Shall be exact since prec(tmp) > prec(rop) */
114 
115   /* Overwrite rop with tmp. */
116   rop[0] = tmp[0];
117 
118   /* The new rop now contains a copy of the old rop, with one extra bit of
119      precision while keeping the old rop "hidden" within its mantissa. */
120 
121   return;
122 }
123 
124 /* This function is called after the applied function.
125    rop is the one prepared in the function above,
126    and contains the result of the applied function.
127    This function restores rop like it was before the
128    calls to mpfr_round_nearest_away_begin while
129    copying it back the result of the applied function
130    and performing additional roundings. */
131 int
mpfr_round_nearest_away_end(mpfr_ptr rop,int inex)132 mpfr_round_nearest_away_end (mpfr_ptr rop, int inex)
133 {
134   mpfr_t    tmp;
135   mp_size_t xsize;
136   mpfr_size_limb_extended_t *ext;
137   mpfr_prec_t n;
138   MPFR_SAVE_EXPO_DECL (expo);
139 
140   /* Get back the hidden context.
141      Note: The cast to void * prevents the compiler from emitting a warning
142      (or error), such as:
143        cast increases required alignment of target type
144      with the -Wcast-align GCC option. Correct alignment is a consequence
145      of the code that built rop.
146   */
147   ext = ((mpfr_size_limb_extended_t *) (void *) MPFR_MANT(rop)) - MANTISSA;
148 
149   /* Create tmp with the result of the function. */
150   tmp[0] = rop[0];
151 
152   /* Revert rop back to what it was before calling
153      mpfr_round_neareset_away_begin. */
154   MPFR_PREC(rop) = ext[OLD_PREC].pr;
155   MPFR_SIGN(rop) = ext[OLD_SIGN].sg;
156   MPFR_EXP(rop)  = ext[OLD_EXPONENT].ex;
157   MPFR_MANT(rop) = ext[OLD_MANTISSA].pi;
158   MPFR_ASSERTD(MPFR_PREC(tmp) == MPFR_PREC(rop)+1);
159 
160   /* Restore the saved context. */
161   expo.saved_flags = ext[OLD_FLAGS].fl;
162   expo.saved_emin  = ext[OLD_EXP_MIN].ex;
163   expo.saved_emax  = ext[OLD_EXP_MAX].ex;
164   xsize            = ext[ALLOC_SIZE].si;
165 
166   /* Perform RNDNA. */
167   n = MPFR_PREC(rop);
168   if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (tmp)))
169     mpfr_set (rop, tmp, MPFR_RNDN); /* inex unchanged */
170   else
171     {
172       int lastbit, sh;
173 
174       MPFR_UNSIGNED_MINUS_MODULO(sh, n + 1);
175       lastbit = (MPFR_MANT(tmp)[0] >> sh) & 1;
176 
177       if (lastbit == 0)
178         mpfr_set (rop, tmp, MPFR_RNDN); /* exact, inex unchanged */
179       else if (inex == 0)  /* midpoint: round away from zero */
180         inex = mpfr_set (rop, tmp, MPFR_RNDA);
181       else  /* lastbit == 1, inex != 0: double rounding */
182         inex = mpfr_set (rop, tmp, (inex > 0) ? MPFR_RNDD : MPFR_RNDU);
183     }
184 
185   MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, __gmpfr_flags);
186   MPFR_SAVE_EXPO_FREE (expo);
187 
188   /* special treatment for the case rop = +/- 2^(emin-2), which should be
189      rounded to +/- 2^(emin-1). We do as if it was rounded to zero, thus the
190      mpfr_check_range() call will round it to +/- 2^(emin-1). */
191   if (inex == 0 && mpfr_cmp_si_2exp (rop, (mpfr_sgn (rop) > 0) ? 1 : -1,
192                                      __gmpfr_emin - 2) == 0)
193     inex = -mpfr_sgn (rop);
194 
195   /* Free tmp (cannot call mpfr_clear): free the associated context. */
196   mpfr_free_func(ext, MPFR_MALLOC_EXTENDED_SIZE(xsize));
197 
198   return mpfr_check_range (rop, inex, MPFR_RNDN);
199 }
200