Home | History | Annotate | Line # | Download | only in src
      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
     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
    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