(root)/
mpfr-4.2.1/
src/
round_near_x.c
       1  /* mpfr_round_near_x -- Round a floating point number nears another one.
       2  
       3  Copyright 2005-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  /* Use MPFR_FAST_COMPUTE_IF_SMALL_INPUT instead (a simple wrapper) */
      26  
      27  /* int mpfr_round_near_x (mpfr_ptr y, mpfr_srcptr v, mpfr_uexp_t err, int dir,
      28                            mpfr_rnd_t rnd)
      29  
      30     TODO: fix this description.
      31     Assuming y = o(f(x)) = o(x + g(x)) with |g(x)| < 2^(EXP(v)-error)
      32     If x is small enough, y ~= v. This function checks and does this.
      33  
      34     It assumes that f(x) is not representable exactly as a FP number.
      35     v must not be a singular value (NAN, INF or ZERO), usual values are
      36     v=1 or v=x.
      37  
      38     y is the destination (a mpfr_t), v the value to set (a mpfr_t),
      39     err the error term (a mpfr_uexp_t) such that |g(x)| < 2^(EXP(x)-err),
      40     dir (an int) is the direction of the error (if dir = 0,
      41     it rounds toward 0, if dir=1, it rounds away from 0),
      42     rnd the rounding mode.
      43  
      44     It returns 0 if it can't round.
      45     Otherwise it returns the ternary flag (It can't return an exact value).
      46  */
      47  
      48  /* What "small enough" means?
      49  
      50     We work with the positive values.
      51     Assuming err > Prec (y)+1
      52  
      53     i = [ y = o(x)]   // i = inexact flag
      54     If i == 0
      55         Setting x in y is exact. We have:
      56         y = [XXXXXXXXX[...]]0[...] + error where [..] are optional zeros
      57        if dirError = ToInf,
      58          x < f(x) < x + 2^(EXP(x)-err)
      59          since x=y, and ulp (y)/2 > 2^(EXP(x)-err), we have:
      60          y < f(x) < y+ulp(y) and |y-f(x)| < ulp(y)/2
      61         if rnd = RNDN, nothing
      62         if rnd = RNDZ, nothing
      63         if rnd = RNDA, addoneulp
      64        elif dirError = ToZero
      65          x -2^(EXP(x)-err) < f(x) < x
      66          since x=y, and ulp (y)/2 > 2^(EXP(x)-err), we have:
      67          y-ulp(y) < f(x) < y and |y-f(x)| < ulp(y)/2
      68         if rnd = RNDN, nothing
      69         if rnd = RNDZ, nexttozero
      70         if rnd = RNDA, nothing
      71       NOTE: err > prec (y)+1 is needed only for RNDN.
      72     elif i > 0 and i = EVEN_ROUNDING
      73        So rnd = RNDN and we have y = x + ulp(y)/2
      74         if dirError = ToZero,
      75           we have x -2^(EXP(x)-err) < f(x) < x
      76           so y - ulp(y)/2 - 2^(EXP(x)-err) < f(x) < y-ulp(y)/2
      77           so y -ulp(y) < f(x) < y-ulp(y)/2
      78           => nexttozero(y)
      79         elif dirError = ToInf
      80           we have x < f(x) < x + 2^(EXP(x)-err)
      81           so y - ulp(y)/2 < f(x) < y+ulp(y)/2-ulp(y)/2
      82           so y - ulp(y)/2 < f(x) < y
      83           => do nothing
      84     elif i < 0 and i = -EVEN_ROUNDING
      85        So rnd = RNDN and we have y = x - ulp(y)/2
      86        if dirError = ToZero,
      87          y < f(x) < y + ulp(y)/2 => do nothing
      88        if dirError = ToInf
      89          y + ulp(y)/2 < f(x) < y + ulp(y) => AddOneUlp
      90     elif i > 0
      91       we can't have rnd = RNDZ, and prec(x) > prec(y), so ulp(x) < ulp(y)
      92       we have y - ulp (y) < x < y
      93       or more exactly y - ulp(y) + ulp(x)/2 <= x <= y - ulp(x)/2
      94       if rnd = RNDA,
      95        if dirError = ToInf,
      96         we have x < f(x) < x + 2^(EXP(x)-err)
      97         if err > prec (x),
      98           we have 2^(EXP(x)-err) < ulp(x), so 2^(EXP(x)-err) <= ulp(x)/2
      99           so f(x) <= y - ulp(x)/2+ulp(x)/2 <= y
     100           and y - ulp(y) < x < f(x)
     101           so we have y - ulp(y) < f(x) < y
     102           so do nothing.
     103         elif we can round, ie y - ulp(y) < x + 2^(EXP(x)-err) < y
     104           we have y - ulp(y) < x <  f(x) < x + 2^(EXP(x)-err) < y
     105           so do nothing
     106         otherwise
     107           Wrong. Example X=[0.11101]111111110000
     108                           +             1111111111111111111....
     109        elif dirError = ToZero
     110         we have x - 2^(EXP(x)-err) < f(x) < x
     111         so f(x) < x < y
     112         if err > prec (x)
     113           x-2^(EXP(x)-err) >= x-ulp(x)/2 >= y - ulp(y) + ulp(x)/2-ulp(x)/2
     114           so y - ulp(y) < f(x) < y
     115           so do nothing
     116         elif we can round, ie y - ulp(y) < x - 2^(EXP(x)-err) < y
     117           y - ulp(y) < x - 2^(EXP(x)-err) < f(x) < y
     118           so do nothing
     119         otherwise
     120          Wrong. Example: X=[1.111010]00000010
     121                           -             10000001000000000000100....
     122       elif rnd = RNDN,
     123        y - ulp(y)/2 < x < y and we can't have x = y-ulp(y)/2:
     124        so we have:
     125         y - ulp(y)/2 + ulp(x)/2 <= x <= y - ulp(x)/2
     126        if dirError = ToInf
     127          we have x < f(x) < x+2^(EXP(x)-err) and ulp(y) > 2^(EXP(x)-err)
     128          so y - ulp(y)/2 + ulp (x)/2 < f(x) < y + ulp (y)/2 - ulp (x)/2
     129          we can round but we can't compute inexact flag.
     130          if err > prec (x)
     131            y - ulp(y)/2 + ulp (x)/2 < f(x) < y + ulp(x)/2 - ulp(x)/2
     132            so y - ulp(y)/2 + ulp (x)/2 < f(x) < y
     133            we can round and compute inexact flag. do nothing
     134          elif we can round, ie y - ulp(y)/2 < x + 2^(EXP(x)-err) < y
     135            we have  y - ulp(y)/2 + ulp (x)/2 < f(x) < y
     136            so do nothing
     137          otherwise
     138            Wrong
     139        elif dirError = ToZero
     140          we have x -2^(EXP(x)-err) < f(x) < x and ulp(y)/2 > 2^(EXP(x)-err)
     141          so y-ulp(y)+ulp(x)/2 < f(x) < y - ulp(x)/2
     142          if err > prec (x)
     143             x- ulp(x)/2 < f(x) < x
     144             so y - ulp(y)/2+ulp(x)/2 - ulp(x)/2 < f(x) < x <= y - ulp(x)/2 < y
     145             do nothing
     146          elif we can round, ie y-ulp(y)/2 < x-2^(EXP(x)-err) < y
     147             we have y-ulp(y)/2 < x-2^(EXP(x)-err) < f(x) < x < y
     148             do nothing
     149          otherwise
     150            Wrong
     151     elif i < 0
     152       same thing?
     153   */
     154  
     155  int
     156  mpfr_round_near_x (mpfr_ptr y, mpfr_srcptr v, mpfr_uexp_t err, int dir,
     157                     mpfr_rnd_t rnd)
     158  {
     159    int inexact, sign;
     160    mpfr_flags_t old_flags = __gmpfr_flags;
     161  
     162    if (rnd == MPFR_RNDF)
     163      rnd = MPFR_RNDZ;
     164  
     165    MPFR_ASSERTD (!MPFR_IS_SINGULAR (v));
     166    MPFR_ASSERTD (dir == 0 || dir == 1);
     167  
     168    /* First check if we can round. The test is more restrictive than
     169       necessary. Note that if err is not representable in an mpfr_exp_t,
     170       then err > MPFR_PREC (v) and the conversion to mpfr_exp_t will not
     171       occur. */
     172    if (!(err > MPFR_PREC (y) + 1
     173          && (err > MPFR_PREC (v)
     174              || mpfr_round_p (MPFR_MANT (v), MPFR_LIMB_SIZE (v),
     175                               (mpfr_exp_t) err,
     176                               MPFR_PREC (y) + (rnd == MPFR_RNDN)))))
     177      /* If we assume we can not round, return 0, and y is not modified */
     178      return 0;
     179  
     180    /* First round v in y */
     181    sign = MPFR_SIGN (v);
     182    MPFR_SET_EXP (y, MPFR_GET_EXP (v));
     183    MPFR_SET_SIGN (y, sign);
     184    MPFR_RNDRAW_GEN (inexact, y, MPFR_MANT (v), MPFR_PREC (v), rnd, sign,
     185                     if (dir == 0)
     186                       {
     187                         inexact = -sign;
     188                         goto trunc_doit;
     189                       }
     190                     else
     191                       goto addoneulp;
     192                     , if (MPFR_UNLIKELY (++MPFR_EXP (y) > __gmpfr_emax))
     193                         mpfr_overflow (y, rnd, sign)
     194                    );
     195  
     196    /* Fix it in some cases */
     197    MPFR_ASSERTD (!MPFR_IS_NAN (y) && !MPFR_IS_ZERO (y));
     198    /* If inexact == 0, setting y from v is exact but we haven't
     199       take into account yet the error term */
     200    if (inexact == 0)
     201      {
     202        if (dir == 0) /* The error term is negative for v positive */
     203          {
     204            inexact = sign;
     205            if (MPFR_IS_LIKE_RNDZ (rnd, MPFR_IS_NEG_SIGN (sign)))
     206              {
     207                /* case nexttozero */
     208                /* The underflow flag should be set if the result is zero */
     209                __gmpfr_flags = old_flags;
     210                inexact = -sign;
     211                mpfr_nexttozero (y);
     212                if (MPFR_UNLIKELY (MPFR_IS_ZERO (y)))
     213                  MPFR_SET_UNDERFLOW ();
     214              }
     215          }
     216        else /* The error term is positive for v positive */
     217          {
     218            inexact = -sign;
     219            /* Round Away */
     220              if (MPFR_IS_LIKE_RNDA (rnd, MPFR_IS_NEG_SIGN(sign)))
     221              {
     222                /* case nexttoinf */
     223                /* The overflow flag should be set if the result is infinity */
     224                inexact = sign;
     225                mpfr_nexttoinf (y);
     226                if (MPFR_UNLIKELY (MPFR_IS_INF (y)))
     227                  MPFR_SET_OVERFLOW ();
     228              }
     229          }
     230      }
     231  
     232    /* the inexact flag cannot be 0, since this would mean an exact value,
     233       and in this case we cannot round correctly */
     234    MPFR_ASSERTD(inexact != 0);
     235    MPFR_RET (inexact);
     236  }