xref: /dflybsd-src/contrib/mpfr/src/subnormal.c (revision 2786097444a0124b5d33763854de247e230c6629)
14a238c70SJohn Marino /* mpfr_subnormalize -- Subnormalize a floating point number
24a238c70SJohn Marino    emulating sub-normal numbers.
34a238c70SJohn Marino 
4*ab6d115fSJohn Marino Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
5*ab6d115fSJohn Marino Contributed by the AriC and Caramel projects, INRIA.
64a238c70SJohn Marino 
74a238c70SJohn Marino This file is part of the GNU MPFR Library.
84a238c70SJohn Marino 
94a238c70SJohn Marino The GNU MPFR Library is free software; you can redistribute it and/or modify
104a238c70SJohn Marino it under the terms of the GNU Lesser General Public License as published by
114a238c70SJohn Marino the Free Software Foundation; either version 3 of the License, or (at your
124a238c70SJohn Marino option) any later version.
134a238c70SJohn Marino 
144a238c70SJohn Marino The GNU MPFR Library is distributed in the hope that it will be useful, but
154a238c70SJohn Marino WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
164a238c70SJohn Marino or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
174a238c70SJohn Marino License for more details.
184a238c70SJohn Marino 
194a238c70SJohn Marino You should have received a copy of the GNU Lesser General Public License
204a238c70SJohn Marino along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
214a238c70SJohn Marino http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
224a238c70SJohn Marino 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
234a238c70SJohn Marino 
244a238c70SJohn Marino #include "mpfr-impl.h"
254a238c70SJohn Marino 
264a238c70SJohn Marino /* For MPFR_RNDN, we can have a problem of double rounding.
274a238c70SJohn Marino    In such a case, this table helps to conclude what to do (y positive):
284a238c70SJohn Marino      Rounding Bit |  Sticky Bit | inexact  | Action    | new inexact
294a238c70SJohn Marino      0            |   ?         |  ?       | Trunc     | sticky
304a238c70SJohn Marino      1            |   0         |  1       | Trunc     |
314a238c70SJohn Marino      1            |   0         |  0       | Trunc if even |
324a238c70SJohn Marino      1            |   0         | -1       | AddOneUlp |
334a238c70SJohn Marino      1            |   1         |  ?       | AddOneUlp |
344a238c70SJohn Marino 
354a238c70SJohn Marino    For other rounding mode, there isn't such a problem.
364a238c70SJohn Marino    Just round it again and merge the ternary values.
374a238c70SJohn Marino 
384a238c70SJohn Marino    Set the inexact flag if the returned ternary value is non-zero.
394a238c70SJohn Marino    Set the underflow flag if a second rounding occurred (whether this
404a238c70SJohn Marino    rounding is exact or not). See
41*ab6d115fSJohn Marino      https://sympa.inria.fr/sympa/arc/mpfr/2009-06/msg00000.html
42*ab6d115fSJohn Marino      https://sympa.inria.fr/sympa/arc/mpfr/2009-06/msg00008.html
43*ab6d115fSJohn Marino      https://sympa.inria.fr/sympa/arc/mpfr/2009-06/msg00010.html
444a238c70SJohn Marino */
454a238c70SJohn Marino 
464a238c70SJohn Marino int
mpfr_subnormalize(mpfr_ptr y,int old_inexact,mpfr_rnd_t rnd)474a238c70SJohn Marino mpfr_subnormalize (mpfr_ptr y, int old_inexact, mpfr_rnd_t rnd)
484a238c70SJohn Marino {
494a238c70SJohn Marino   int sign;
504a238c70SJohn Marino 
514a238c70SJohn Marino   /* The subnormal exponent range is [ emin, emin + MPFR_PREC(y) - 2 ] */
524a238c70SJohn Marino   if (MPFR_LIKELY (MPFR_IS_SINGULAR (y)
534a238c70SJohn Marino                    || (MPFR_GET_EXP (y) >=
544a238c70SJohn Marino                        __gmpfr_emin + (mpfr_exp_t) MPFR_PREC (y) - 1)))
554a238c70SJohn Marino     MPFR_RET (old_inexact);
564a238c70SJohn Marino 
574a238c70SJohn Marino   mpfr_set_underflow ();
584a238c70SJohn Marino   sign = MPFR_SIGN (y);
594a238c70SJohn Marino 
604a238c70SJohn Marino   /* We have to emulate one bit rounding if EXP(y) = emin */
614a238c70SJohn Marino   if (MPFR_GET_EXP (y) == __gmpfr_emin)
624a238c70SJohn Marino     {
634a238c70SJohn Marino       /* If this is a power of 2, we don't need rounding.
644a238c70SJohn Marino          It handles cases when |y| = 0.1 * 2^emin */
654a238c70SJohn Marino       if (mpfr_powerof2_raw (y))
664a238c70SJohn Marino         MPFR_RET (old_inexact);
674a238c70SJohn Marino 
684a238c70SJohn Marino       /* We keep the same sign for y.
694a238c70SJohn Marino          Assuming Y is the real value and y the approximation
704a238c70SJohn Marino          and since y is not a power of 2:  0.5*2^emin < Y < 1*2^emin
714a238c70SJohn Marino          We also know the direction of the error thanks to ternary value. */
724a238c70SJohn Marino 
734a238c70SJohn Marino       if (rnd == MPFR_RNDN)
744a238c70SJohn Marino         {
754a238c70SJohn Marino           mp_limb_t *mant, rb ,sb;
764a238c70SJohn Marino           mp_size_t s;
774a238c70SJohn Marino           /* We need the rounding bit and the sticky bit. Read them
784a238c70SJohn Marino              and use the previous table to conclude. */
794a238c70SJohn Marino           s = MPFR_LIMB_SIZE (y) - 1;
804a238c70SJohn Marino           mant = MPFR_MANT (y) + s;
814a238c70SJohn Marino           rb = *mant & (MPFR_LIMB_HIGHBIT >> 1);
824a238c70SJohn Marino           if (rb == 0)
834a238c70SJohn Marino             goto set_min;
844a238c70SJohn Marino           sb = *mant & ((MPFR_LIMB_HIGHBIT >> 1) - 1);
854a238c70SJohn Marino           while (sb == 0 && s-- != 0)
864a238c70SJohn Marino             sb = *--mant;
874a238c70SJohn Marino           if (sb != 0)
884a238c70SJohn Marino             goto set_min_p1;
894a238c70SJohn Marino           /* Rounding bit is 1 and sticky bit is 0.
904a238c70SJohn Marino              We need to examine old inexact flag to conclude. */
914a238c70SJohn Marino           if ((old_inexact > 0 && sign > 0) ||
924a238c70SJohn Marino               (old_inexact < 0 && sign < 0))
934a238c70SJohn Marino             goto set_min;
944a238c70SJohn Marino           /* If inexact != 0, return 0.1*2^(emin+1).
954a238c70SJohn Marino              Otherwise, rounding bit = 1, sticky bit = 0 and inexact = 0
964a238c70SJohn Marino              So we have 0.1100000000000000000000000*2^emin exactly.
974a238c70SJohn Marino              We return 0.1*2^(emin+1) according to the even-rounding
984a238c70SJohn Marino              rule on subnormals. */
994a238c70SJohn Marino           goto set_min_p1;
1004a238c70SJohn Marino         }
1014a238c70SJohn Marino       else if (MPFR_IS_LIKE_RNDZ (rnd, MPFR_IS_NEG (y)))
1024a238c70SJohn Marino         {
1034a238c70SJohn Marino         set_min:
1044a238c70SJohn Marino           mpfr_setmin (y, __gmpfr_emin);
1054a238c70SJohn Marino           MPFR_RET (-sign);
1064a238c70SJohn Marino         }
1074a238c70SJohn Marino       else
1084a238c70SJohn Marino         {
1094a238c70SJohn Marino         set_min_p1:
1104a238c70SJohn Marino           /* Note: mpfr_setmin will abort if __gmpfr_emax == __gmpfr_emin. */
1114a238c70SJohn Marino           mpfr_setmin (y, __gmpfr_emin + 1);
1124a238c70SJohn Marino           MPFR_RET (sign);
1134a238c70SJohn Marino         }
1144a238c70SJohn Marino     }
1154a238c70SJohn Marino   else /* Hard case: It is more or less the same problem than mpfr_cache */
1164a238c70SJohn Marino     {
1174a238c70SJohn Marino       mpfr_t dest;
1184a238c70SJohn Marino       mpfr_prec_t q;
1194a238c70SJohn Marino       int inexact, inex2;
1204a238c70SJohn Marino 
1214a238c70SJohn Marino       MPFR_ASSERTD (MPFR_GET_EXP (y) > __gmpfr_emin);
1224a238c70SJohn Marino 
1234a238c70SJohn Marino       /* Compute the intermediary precision */
1244a238c70SJohn Marino       q = (mpfr_uexp_t) MPFR_GET_EXP (y) - __gmpfr_emin + 1;
1254a238c70SJohn Marino       MPFR_ASSERTD (q >= MPFR_PREC_MIN && q < MPFR_PREC (y));
1264a238c70SJohn Marino 
1274a238c70SJohn Marino       /* TODO: perform the rounding in place. */
1284a238c70SJohn Marino       mpfr_init2 (dest, q);
1294a238c70SJohn Marino       /* Round y in dest */
1304a238c70SJohn Marino       MPFR_SET_EXP (dest, MPFR_GET_EXP (y));
1314a238c70SJohn Marino       MPFR_SET_SIGN (dest, sign);
1324a238c70SJohn Marino       MPFR_RNDRAW_EVEN (inexact, dest,
1334a238c70SJohn Marino                         MPFR_MANT (y), MPFR_PREC (y), rnd, sign,
1344a238c70SJohn Marino                         MPFR_SET_EXP (dest, MPFR_GET_EXP (dest) + 1));
1354a238c70SJohn Marino       if (MPFR_LIKELY (old_inexact != 0))
1364a238c70SJohn Marino         {
1374a238c70SJohn Marino           if (MPFR_UNLIKELY (rnd == MPFR_RNDN &&
1384a238c70SJohn Marino                              (inexact == MPFR_EVEN_INEX ||
1394a238c70SJohn Marino                               inexact == -MPFR_EVEN_INEX)))
1404a238c70SJohn Marino             {
1414a238c70SJohn Marino               /* if both roundings are in the same direction, we have to go
1424a238c70SJohn Marino                  back in the other direction */
1434a238c70SJohn Marino               if (SAME_SIGN (inexact, old_inexact))
1444a238c70SJohn Marino                 {
1454a238c70SJohn Marino                   if (SAME_SIGN (inexact, MPFR_INT_SIGN (y)))
1464a238c70SJohn Marino                     mpfr_nexttozero (dest);
1474a238c70SJohn Marino                   else
1484a238c70SJohn Marino                     mpfr_nexttoinf (dest);
1494a238c70SJohn Marino                   inexact = -inexact;
1504a238c70SJohn Marino                 }
1514a238c70SJohn Marino             }
1524a238c70SJohn Marino           else if (MPFR_UNLIKELY (inexact == 0))
1534a238c70SJohn Marino             inexact = old_inexact;
1544a238c70SJohn Marino         }
1554a238c70SJohn Marino 
1564a238c70SJohn Marino       inex2 = mpfr_set (y, dest, rnd);
1574a238c70SJohn Marino       MPFR_ASSERTN (inex2 == 0);
1584a238c70SJohn Marino       MPFR_ASSERTN (MPFR_IS_PURE_FP (y));
1594a238c70SJohn Marino       mpfr_clear (dest);
1604a238c70SJohn Marino 
1614a238c70SJohn Marino       MPFR_RET (inexact);
1624a238c70SJohn Marino     }
1634a238c70SJohn Marino }
164