]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/rint.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / rint.c
diff --git a/mpfr/rint.c b/mpfr/rint.c
new file mode 100644 (file)
index 0000000..6ca1a3b
--- /dev/null
@@ -0,0 +1,435 @@
+/* mpfr_rint -- Round to an integer.
+
+Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+Contributed by the Arenaire and Cacao projects, INRIA.
+
+This file is part of the GNU MPFR Library.
+
+The GNU MPFR Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The GNU MPFR Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the GNU MPFR Library; see the file COPYING.LIB.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include "mpfr-impl.h"
+
+/* Merge the following mpfr_rint code with mpfr_round_raw_generic? */
+
+int
+mpfr_rint (mpfr_ptr r, mpfr_srcptr u, mpfr_rnd_t rnd_mode)
+{
+  int sign;
+  int rnd_away;
+  mp_exp_t exp;
+
+  if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(u) ))
+    {
+      if (MPFR_IS_NAN(u))
+        {
+          MPFR_SET_NAN(r);
+          MPFR_RET_NAN;
+        }
+      MPFR_SET_SAME_SIGN(r, u);
+      if (MPFR_IS_INF(u))
+        {
+          MPFR_SET_INF(r);
+          MPFR_RET(0);  /* infinity is exact */
+        }
+      else /* now u is zero */
+        {
+          MPFR_ASSERTD(MPFR_IS_ZERO(u));
+          MPFR_SET_ZERO(r);
+          MPFR_RET(0);  /* zero is exact */
+        }
+    }
+  MPFR_SET_SAME_SIGN (r, u); /* Does nothing if r==u */
+
+  sign = MPFR_INT_SIGN (u);
+  exp = MPFR_GET_EXP (u);
+
+  rnd_away =
+    rnd_mode == GMP_RNDD ? sign < 0 :
+    rnd_mode == GMP_RNDU ? sign > 0 :
+    rnd_mode == GMP_RNDZ ? 0 : -1;
+
+  /* rnd_away:
+     1 if round away from zero,
+     0 if round to zero,
+     -1 if not decided yet.
+   */
+
+  if (MPFR_UNLIKELY (exp <= 0))  /* 0 < |u| < 1 ==> round |u| to 0 or 1 */
+    {
+      /* Note: in the GMP_RNDN mode, 0.5 must be rounded to 0. */
+      if (rnd_away != 0 &&
+          (rnd_away > 0 ||
+           (exp == 0 && (rnd_mode == GMP_RNDNA ||
+                         !mpfr_powerof2_raw (u)))))
+        {
+          mp_limb_t *rp;
+          mp_size_t rm;
+
+          rp = MPFR_MANT(r);
+          rm = (MPFR_PREC(r) - 1) / BITS_PER_MP_LIMB;
+          rp[rm] = MPFR_LIMB_HIGHBIT;
+          MPN_ZERO(rp, rm);
+          MPFR_SET_EXP (r, 1);  /* |r| = 1 */
+          MPFR_RET(sign > 0 ? 2 : -2);
+        }
+      else
+        {
+          MPFR_SET_ZERO(r);  /* r = 0 */
+          MPFR_RET(sign > 0 ? -2 : 2);
+        }
+    }
+  else  /* exp > 0, |u| >= 1 */
+    {
+      mp_limb_t *up, *rp;
+      mp_size_t un, rn, ui;
+      int sh, idiff;
+      int uflags;
+
+      /*
+       * uflags will contain:
+       *   _ 0 if u is an integer representable in r,
+       *   _ 1 if u is an integer not representable in r,
+       *   _ 2 if u is not an integer.
+       */
+
+      up = MPFR_MANT(u);
+      rp = MPFR_MANT(r);
+
+      un = MPFR_LIMB_SIZE(u);
+      rn = MPFR_LIMB_SIZE(r);
+      MPFR_UNSIGNED_MINUS_MODULO (sh, MPFR_PREC (r));
+
+      MPFR_SET_EXP (r, exp); /* Does nothing if r==u */
+
+      if ((exp - 1) / BITS_PER_MP_LIMB >= un)
+        {
+          ui = un;
+          idiff = 0;
+          uflags = 0;  /* u is an integer, representable or not in r */
+        }
+      else
+        {
+          mp_size_t uj;
+
+          ui = (exp - 1) / BITS_PER_MP_LIMB + 1;  /* #limbs of the int part */
+          MPFR_ASSERTD (un >= ui);
+          uj = un - ui;  /* lowest limb of the integer part */
+          idiff = exp % BITS_PER_MP_LIMB;  /* #int-part bits in up[uj] or 0 */
+
+          uflags = idiff == 0 || (up[uj] << idiff) == 0 ? 0 : 2;
+          if (uflags == 0)
+            while (uj > 0)
+              if (up[--uj] != 0)
+                {
+                  uflags = 2;
+                  break;
+                }
+        }
+
+      if (ui > rn)
+        {
+          /* More limbs in the integer part of u than in r.
+             Just round u with the precision of r. */
+          MPFR_ASSERTD (rp != up && un > rn);
+          MPN_COPY (rp, up + (un - rn), rn); /* r != u */
+          if (rnd_away < 0)
+            {
+              /* This is a rounding to nearest mode (GMP_RNDN or GMP_RNDNA).
+                 Decide the rounding direction here. */
+              if (rnd_mode == GMP_RNDN &&
+                  (rp[0] & (MPFR_LIMB_ONE << sh)) == 0)
+                { /* halfway cases rounded towards zero */
+                  mp_limb_t a, b;
+                  /* a: rounding bit and some of the following bits */
+                  /* b: boundary for a (weight of the rounding bit in a) */
+                  if (sh != 0)
+                    {
+                      a = rp[0] & ((MPFR_LIMB_ONE << sh) - 1);
+                      b = MPFR_LIMB_ONE << (sh - 1);
+                    }
+                  else
+                    {
+                      a = up[un - rn - 1];
+                      b = MPFR_LIMB_HIGHBIT;
+                    }
+                  rnd_away = a > b;
+                  if (a == b)
+                    {
+                      mp_size_t i;
+                      for (i = un - rn - 1 - (sh == 0); i >= 0; i--)
+                        if (up[i] != 0)
+                          {
+                            rnd_away = 1;
+                            break;
+                          }
+                    }
+                }
+              else  /* halfway cases rounded away from zero */
+                rnd_away =  /* rounding bit */
+                  ((sh != 0 && (rp[0] & (MPFR_LIMB_ONE << (sh - 1))) != 0) ||
+                   (sh == 0 && (up[un - rn - 1] & MPFR_LIMB_HIGHBIT) != 0));
+            }
+          if (uflags == 0)
+            { /* u is an integer; determine if it is representable in r */
+              if (sh != 0 && rp[0] << (BITS_PER_MP_LIMB - sh) != 0)
+                uflags = 1;  /* u is not representable in r */
+              else
+                {
+                  mp_size_t i;
+                  for (i = un - rn - 1; i >= 0; i--)
+                    if (up[i] != 0)
+                      {
+                        uflags = 1;  /* u is not representable in r */
+                        break;
+                      }
+                }
+            }
+        }
+      else  /* ui <= rn */
+        {
+          mp_size_t uj, rj;
+          int ush;
+
+          uj = un - ui;  /* lowest limb of the integer part in u */
+          rj = rn - ui;  /* lowest limb of the integer part in r */
+
+          if (MPFR_LIKELY (rp != up))
+            MPN_COPY(rp + rj, up + uj, ui);
+
+          /* Ignore the lowest rj limbs, all equal to zero. */
+          rp += rj;
+          rn = ui;
+
+          /* number of fractional bits in whole rp[0] */
+          ush = idiff == 0 ? 0 : BITS_PER_MP_LIMB - idiff;
+
+          if (rj == 0 && ush < sh)
+            {
+              /* If u is an integer (uflags == 0), we need to determine
+                 if it is representable in r, i.e. if its sh - ush bits
+                 in the non-significant part of r are all 0. */
+              if (uflags == 0 && (rp[0] & ((MPFR_LIMB_ONE << sh) -
+                                           (MPFR_LIMB_ONE << ush))) != 0)
+                uflags = 1;  /* u is an integer not representable in r */
+            }
+          else  /* The integer part of u fits in r, we'll round to it. */
+            sh = ush;
+
+          if (rnd_away < 0)
+            {
+              /* This is a rounding to nearest mode.
+                 Decide the rounding direction here. */
+              if (uj == 0 && sh == 0)
+                rnd_away = 0; /* rounding bit = 0 (not represented in u) */
+              else if (rnd_mode == GMP_RNDN &&
+                       (rp[0] & (MPFR_LIMB_ONE << sh)) == 0)
+                { /* halfway cases rounded towards zero */
+                  mp_limb_t a, b;
+                  /* a: rounding bit and some of the following bits */
+                  /* b: boundary for a (weight of the rounding bit in a) */
+                  if (sh != 0)
+                    {
+                      a = rp[0] & ((MPFR_LIMB_ONE << sh) - 1);
+                      b = MPFR_LIMB_ONE << (sh - 1);
+                    }
+                  else
+                    {
+                      MPFR_ASSERTD (uj >= 1);  /* see above */
+                      a = up[uj - 1];
+                      b = MPFR_LIMB_HIGHBIT;
+                    }
+                  rnd_away = a > b;
+                  if (a == b)
+                    {
+                      mp_size_t i;
+                      for (i = uj - 1 - (sh == 0); i >= 0; i--)
+                        if (up[i] != 0)
+                          {
+                            rnd_away = 1;
+                            break;
+                          }
+                    }
+                }
+              else  /* halfway cases rounded away from zero */
+                rnd_away =  /* rounding bit */
+                  ((sh != 0 && (rp[0] & (MPFR_LIMB_ONE << (sh - 1))) != 0) ||
+                   (sh == 0 && (MPFR_ASSERTD (uj >= 1),
+                                up[uj - 1] & MPFR_LIMB_HIGHBIT) != 0));
+            }
+          /* Now we can make the low rj limbs to 0 */
+          MPN_ZERO (rp-rj, rj);
+        }
+
+      if (sh != 0)
+        rp[0] &= MP_LIMB_T_MAX << sh;
+
+      /* If u is a representable integer, there is no rounding. */
+      if (uflags == 0)
+        MPFR_RET(0);
+
+      MPFR_ASSERTD (rnd_away >= 0);  /* rounding direction is defined */
+      if (rnd_away && mpn_add_1(rp, rp, rn, MPFR_LIMB_ONE << sh))
+        {
+          if (exp == __gmpfr_emax)
+            return mpfr_overflow(r, rnd_mode, MPFR_SIGN(r)) >= 0 ?
+              uflags : -uflags;
+          else
+            {
+              MPFR_SET_EXP(r, exp + 1);
+              rp[rn-1] = MPFR_LIMB_HIGHBIT;
+            }
+        }
+
+      MPFR_RET (rnd_away ^ (sign < 0) ? uflags : -uflags);
+    }  /* exp > 0, |u| >= 1 */
+}
+
+#undef mpfr_round
+
+int
+mpfr_round (mpfr_ptr r, mpfr_srcptr u)
+{
+  return mpfr_rint(r, u, GMP_RNDNA);
+}
+
+#undef mpfr_trunc
+
+int
+mpfr_trunc (mpfr_ptr r, mpfr_srcptr u)
+{
+  return mpfr_rint(r, u, GMP_RNDZ);
+}
+
+#undef mpfr_ceil
+
+int
+mpfr_ceil (mpfr_ptr r, mpfr_srcptr u)
+{
+  return mpfr_rint(r, u, GMP_RNDU);
+}
+
+#undef mpfr_floor
+
+int
+mpfr_floor (mpfr_ptr r, mpfr_srcptr u)
+{
+  return mpfr_rint(r, u, GMP_RNDD);
+}
+
+#undef mpfr_rint_round
+
+int
+mpfr_rint_round (mpfr_ptr r, mpfr_srcptr u, mpfr_rnd_t rnd_mode)
+{
+  if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(u) ) || mpfr_integer_p (u))
+    return mpfr_set (r, u, rnd_mode);
+  else
+    {
+      mpfr_t tmp;
+      int inex;
+      MPFR_SAVE_EXPO_DECL (expo);
+      MPFR_BLOCK_DECL (flags);
+
+      MPFR_SAVE_EXPO_MARK (expo);
+      mpfr_init2 (tmp, MPFR_PREC (u));
+      /* round(u) is representable in tmp unless an overflow occurs */
+      MPFR_BLOCK (flags, mpfr_round (tmp, u));
+      inex = (MPFR_OVERFLOW (flags)
+              ? mpfr_overflow (r, rnd_mode, MPFR_SIGN (u))
+              : mpfr_set (r, tmp, rnd_mode));
+      mpfr_clear (tmp);
+      MPFR_SAVE_EXPO_FREE (expo);
+      return mpfr_check_range (r, inex, rnd_mode);
+    }
+}
+
+#undef mpfr_rint_trunc
+
+int
+mpfr_rint_trunc (mpfr_ptr r, mpfr_srcptr u, mpfr_rnd_t rnd_mode)
+{
+  if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(u) ) || mpfr_integer_p (u))
+    return mpfr_set (r, u, rnd_mode);
+  else
+    {
+      mpfr_t tmp;
+      int inex;
+      MPFR_SAVE_EXPO_DECL (expo);
+
+      MPFR_SAVE_EXPO_MARK (expo);
+      mpfr_init2 (tmp, MPFR_PREC (u));
+      /* trunc(u) is always representable in tmp */
+      mpfr_trunc (tmp, u);
+      inex = mpfr_set (r, tmp, rnd_mode);
+      mpfr_clear (tmp);
+      MPFR_SAVE_EXPO_FREE (expo);
+      return mpfr_check_range (r, inex, rnd_mode);
+    }
+}
+
+#undef mpfr_rint_ceil
+
+int
+mpfr_rint_ceil (mpfr_ptr r, mpfr_srcptr u, mpfr_rnd_t rnd_mode)
+{
+  if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(u) ) || mpfr_integer_p (u))
+    return mpfr_set (r, u, rnd_mode);
+  else
+    {
+      mpfr_t tmp;
+      int inex;
+      MPFR_SAVE_EXPO_DECL (expo);
+      MPFR_BLOCK_DECL (flags);
+
+      MPFR_SAVE_EXPO_MARK (expo);
+      mpfr_init2 (tmp, MPFR_PREC (u));
+      /* ceil(u) is representable in tmp unless an overflow occurs */
+      MPFR_BLOCK (flags, mpfr_ceil (tmp, u));
+      inex = (MPFR_OVERFLOW (flags)
+              ? mpfr_overflow (r, rnd_mode, MPFR_SIGN_POS)
+              : mpfr_set (r, tmp, rnd_mode));
+      mpfr_clear (tmp);
+      MPFR_SAVE_EXPO_FREE (expo);
+      return mpfr_check_range (r, inex, rnd_mode);
+    }
+}
+
+#undef mpfr_rint_floor
+
+int
+mpfr_rint_floor (mpfr_ptr r, mpfr_srcptr u, mpfr_rnd_t rnd_mode)
+{
+  if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(u) ) || mpfr_integer_p (u))
+    return mpfr_set (r, u, rnd_mode);
+  else
+    {
+      mpfr_t tmp;
+      int inex;
+      MPFR_SAVE_EXPO_DECL (expo);
+      MPFR_BLOCK_DECL (flags);
+
+      MPFR_SAVE_EXPO_MARK (expo);
+      mpfr_init2 (tmp, MPFR_PREC (u));
+      /* floor(u) is representable in tmp unless an overflow occurs */
+      MPFR_BLOCK (flags, mpfr_floor (tmp, u));
+      inex = (MPFR_OVERFLOW (flags)
+              ? mpfr_overflow (r, rnd_mode, MPFR_SIGN_NEG)
+              : mpfr_set (r, tmp, rnd_mode));
+      mpfr_clear (tmp);
+      MPFR_SAVE_EXPO_FREE (expo);
+      return mpfr_check_range (r, inex, rnd_mode);
+    }
+}