Home | History | Annotate | Line # | Download | only in src
      1 /* mpfr_round_raw_generic -- Generic rounding function
      2 
      3 Copyright 1999-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 #ifndef flag
     24 # error "ERROR: flag must be defined (0 / 1)"
     25 #endif
     26 #ifndef use_inexp
     27 # error "ERROR: use_enexp must be defined (0 / 1)"
     28 #endif
     29 #ifndef mpfr_round_raw_generic
     30 # error "ERROR: mpfr_round_raw_generic must be defined"
     31 #endif
     32 
     33 /*
     34  * If flag = 0, puts in y the value of xp (with precision xprec and
     35  * sign 1 if negative=0, -1 otherwise) rounded to precision yprec and
     36  * direction rnd_mode. Supposes x is not zero nor NaN nor +/- Infinity
     37  * (i.e. *xp != 0). In that case, the return value is a possible carry
     38  * (0 or 1) that may happen during the rounding, in which case the result
     39  * is a power of two.
     40  *
     41  * If inexp != NULL, put in *inexp the inexact flag of the rounding (0, 1, -1).
     42  * In case of even rounding when rnd = MPFR_RNDN, put MPFR_EVEN_INEX (2) or
     43  * -MPFR_EVEN_INEX (-2) in *inexp.
     44  *
     45  * If flag = 1, just returns whether one should add 1 or not for rounding.
     46  *
     47  * Note: yprec may be < MPFR_PREC_MIN; in particular, it may be equal
     48  * to 1. In this case, the even rounding is done away from 0, which is
     49  * a natural generalization. Indeed, a number with 1-bit precision can
     50  * be seen as a subnormal number with more precision.
     51  *
     52  * MPFR_RNDNA is now supported, but needs to be tested [TODO] and is
     53  * still not part of the API. In particular, the MPFR_RNDNA value (-1)
     54  * may change in the future without notice.
     55  */
     56 
     57 #if !(flag == 0 || flag == 1)
     58 #error "flag must be 0 or 1"
     59 #endif
     60 
     61 int
     62 mpfr_round_raw_generic(
     63 #if flag == 0
     64                        mp_limb_t *yp,
     65 #endif
     66                        const mp_limb_t *xp, mpfr_prec_t xprec,
     67                        int neg, mpfr_prec_t yprec, mpfr_rnd_t rnd_mode
     68 #if use_inexp != 0
     69                        , int *inexp
     70 #endif
     71                        )
     72 {
     73   mp_size_t xsize, nw;
     74   mp_limb_t himask, lomask, sb;
     75   int rw, new_use_inexp;
     76 #if flag == 0
     77   int carry;
     78 #endif
     79 
     80 #if use_inexp != 0
     81   MPFR_ASSERTD (inexp != ((int*) 0));
     82 #endif
     83   MPFR_ASSERTD (neg == 0 || neg == 1);
     84 #if flag == 1
     85   /* rnd_mode = RNDF is only possible for flag = 0. */
     86   MPFR_ASSERTD (rnd_mode != MPFR_RNDF);
     87 #endif
     88 
     89   if (rnd_mode == MPFR_RNDF)
     90     {
     91 #if use_inexp != 0
     92       *inexp = 0;  /* make sure it has a valid value */
     93 #endif
     94       rnd_mode = MPFR_RNDZ;  /* faster */
     95       new_use_inexp = 0;
     96     }
     97   else
     98     {
     99       if (flag && !use_inexp &&
    100           (xprec <= yprec || MPFR_IS_LIKE_RNDZ (rnd_mode, neg)))
    101         return 0;
    102       new_use_inexp = use_inexp;
    103     }
    104 
    105   xsize = MPFR_PREC2LIMBS (xprec);
    106   nw = yprec / GMP_NUMB_BITS;
    107   rw = yprec & (GMP_NUMB_BITS - 1);
    108 
    109   if (MPFR_UNLIKELY(xprec <= yprec))
    110     { /* No rounding is necessary. */
    111       /* if yp=xp, maybe an overlap: mpn_copyd is OK when src <= dst */
    112       if (MPFR_LIKELY(rw))
    113         nw++;
    114       MPFR_ASSERTD(nw >= 1);
    115       MPFR_ASSERTD(nw >= xsize);
    116 #if use_inexp != 0
    117       *inexp = 0;
    118 #endif
    119 #if flag == 0
    120       mpn_copyd (yp + (nw - xsize), xp, xsize);
    121       MPN_ZERO(yp, nw - xsize);
    122 #endif
    123       return 0;
    124     }
    125 
    126   if (new_use_inexp || !MPFR_IS_LIKE_RNDZ(rnd_mode, neg))
    127     {
    128       mp_size_t k = xsize - nw - 1;
    129 
    130       if (MPFR_LIKELY(rw))
    131         {
    132           nw++;
    133           lomask = MPFR_LIMB_MASK (GMP_NUMB_BITS - rw);
    134           himask = ~lomask;
    135         }
    136       else
    137         {
    138           lomask = MPFR_LIMB_MAX;
    139           himask = MPFR_LIMB_MAX;
    140         }
    141       MPFR_ASSERTD(k >= 0);
    142       sb = xp[k] & lomask;  /* First non-significant bits */
    143       /* Rounding to nearest? */
    144       if (rnd_mode == MPFR_RNDN || rnd_mode == MPFR_RNDNA)
    145         {
    146           /* Rounding to nearest */
    147           mp_limb_t rbmask = MPFR_LIMB_ONE << (GMP_NUMB_BITS - 1 - rw);
    148 
    149           if ((sb & rbmask) == 0) /* rounding bit = 0 ? */
    150             goto rnd_RNDZ; /* yes, behave like rounding toward zero */
    151           /* Rounding to nearest with rounding bit = 1 */
    152           if (MPFR_UNLIKELY (rnd_mode == MPFR_RNDNA))
    153             goto away_addone_ulp; /* like rounding away from zero */
    154           sb &= ~rbmask; /* first bits after the rounding bit */
    155           while (MPFR_UNLIKELY(sb == 0) && k > 0)
    156             sb = xp[--k];
    157           if (MPFR_UNLIKELY(sb == 0)) /* Even rounding. */
    158             {
    159               /* sb == 0 && rnd_mode == MPFR_RNDN */
    160               sb = xp[xsize - nw] & (himask ^ (himask << 1));
    161               if (sb == 0)
    162                 {
    163 #if use_inexp != 0
    164                   *inexp = 2 * MPFR_EVEN_INEX * neg - MPFR_EVEN_INEX;
    165 #endif
    166                   /* ((neg!=0)^(sb!=0)) ? MPFR_EVEN_INEX : -MPFR_EVEN_INEX */
    167                   /* since neg = 0 or 1 and sb = 0 */
    168 #if flag == 0
    169                   mpn_copyi (yp, xp + xsize - nw, nw);
    170                   yp[0] &= himask;
    171 #endif
    172                   return 0; /* sb != 0 && rnd_mode != MPFR_RNDZ */
    173                 }
    174               else
    175                 {
    176                 away_addone_ulp:
    177                   /* sb != 0 && rnd_mode == MPFR_RNDN */
    178 #if use_inexp != 0
    179                   *inexp = MPFR_EVEN_INEX - 2 * MPFR_EVEN_INEX * neg;
    180 #endif
    181                   /* ((neg!=0)^(sb!=0)) ? MPFR_EVEN_INEX : -MPFR_EVEN_INEX */
    182                   /* since neg = 0 or 1 and sb != 0 */
    183                   goto rnd_RNDN_add_one_ulp;
    184                 }
    185             }
    186           else /* sb != 0 && rnd_mode == MPFR_RNDN */
    187             {
    188 #if use_inexp != 0
    189               *inexp = 1 - 2 * neg; /* neg == 0 ? 1 : -1 */
    190 #endif
    191             rnd_RNDN_add_one_ulp:
    192 #if flag == 1
    193               return 1; /* sb != 0 && rnd_mode != MPFR_RNDZ */
    194 #else
    195               carry = mpn_add_1 (yp, xp + xsize - nw, nw,
    196                                  rw ?
    197                                  MPFR_LIMB_ONE << (GMP_NUMB_BITS - rw)
    198                                  : MPFR_LIMB_ONE);
    199               yp[0] &= himask;
    200               return carry;
    201 #endif
    202             }
    203         }
    204       /* Rounding toward zero? */
    205       else if (MPFR_IS_LIKE_RNDZ(rnd_mode, neg))
    206         {
    207           /* rnd_mode == MPFR_RNDZ */
    208         rnd_RNDZ:
    209           while (MPFR_UNLIKELY (sb == 0) && k > 0)
    210             sb = xp[--k];
    211 #if use_inexp != 0
    212           /* rnd_mode == MPFR_RNDZ and neg = 0 or 1 */
    213           /* ((neg != 0) ^ (rnd_mode != MPFR_RNDZ)) ? 1 : -1 */
    214           *inexp = MPFR_UNLIKELY (sb == 0) ? 0 : 2 * neg - 1;
    215 #endif
    216 #if flag == 0
    217           mpn_copyi (yp, xp + xsize - nw, nw);
    218           yp[0] &= himask;
    219 #endif
    220           return 0; /* sb != 0 && rnd_mode != MPFR_RNDZ */
    221         }
    222       else
    223         {
    224           /* Rounding away from zero */
    225           while (MPFR_UNLIKELY (sb == 0) && k > 0)
    226             sb = xp[--k];
    227           if (MPFR_UNLIKELY (sb == 0))
    228             {
    229               /* sb = 0 && rnd_mode != MPFR_RNDZ */
    230 #if use_inexp != 0
    231               /* ((neg != 0) ^ (rnd_mode != MPFR_RNDZ)) ? 1 : -1 */
    232               *inexp = 0;
    233 #endif
    234 #if flag == 0
    235               mpn_copyi (yp, xp + xsize - nw, nw);
    236               yp[0] &= himask;
    237 #endif
    238               return 0;
    239             }
    240           else
    241             {
    242               /* sb != 0 && rnd_mode != MPFR_RNDZ */
    243 #if use_inexp != 0
    244               *inexp = 1 - 2 * neg; /* neg == 0 ? 1 : -1 */
    245 #endif
    246 #if flag == 1
    247               return 1;
    248 #else
    249               carry = mpn_add_1(yp, xp + xsize - nw, nw,
    250                                 rw ? MPFR_LIMB_ONE << (GMP_NUMB_BITS - rw)
    251                                 : MPFR_LIMB_ONE);
    252               yp[0] &= himask;
    253               return carry;
    254 #endif
    255             }
    256         }
    257     }
    258   else
    259     {
    260       /* Rounding toward zero / no inexact flag */
    261 #if flag == 0
    262       if (MPFR_LIKELY(rw))
    263         {
    264           nw++;
    265           himask = ~MPFR_LIMB_MASK (GMP_NUMB_BITS - rw);
    266         }
    267       else
    268         himask = MPFR_LIMB_MAX;
    269       mpn_copyi (yp, xp + xsize - nw, nw);
    270       yp[0] &= himask;
    271 #endif
    272       return 0;
    273     }
    274 }
    275 
    276 #undef flag
    277 #undef use_inexp
    278 #undef mpfr_round_raw_generic
    279