Blame lib/frexp.c

Packit Service a2489d
/* Split a double into fraction and mantissa.
Packit Service a2489d
   Copyright (C) 2007-2018 Free Software Foundation, Inc.
Packit Service a2489d
Packit Service a2489d
   This program is free software: you can redistribute it and/or modify
Packit Service a2489d
   it under the terms of the GNU General Public License as published by
Packit Service a2489d
   the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
   (at your option) any later version.
Packit Service a2489d
Packit Service a2489d
   This program is distributed in the hope that it will be useful,
Packit Service a2489d
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
   GNU General Public License for more details.
Packit Service a2489d
Packit Service a2489d
   You should have received a copy of the GNU General Public License
Packit Service a2489d
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2489d
Packit Service a2489d
/* Written by Paolo Bonzini <bonzini@gnu.org>, 2003, and
Packit Service a2489d
   Bruno Haible <bruno@clisp.org>, 2007.  */
Packit Service a2489d
Packit Service a2489d
#if ! defined USE_LONG_DOUBLE
Packit Service a2489d
# include <config.h>
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
/* Specification.  */
Packit Service a2489d
#include <math.h>
Packit Service a2489d
Packit Service a2489d
#include <float.h>
Packit Service a2489d
#ifdef USE_LONG_DOUBLE
Packit Service a2489d
# include "isnanl-nolibm.h"
Packit Service a2489d
# include "fpucw.h"
Packit Service a2489d
#else
Packit Service a2489d
# include "isnand-nolibm.h"
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
/* This file assumes FLT_RADIX = 2.  If FLT_RADIX is a power of 2 greater
Packit Service a2489d
   than 2, or not even a power of 2, some rounding errors can occur, so that
Packit Service a2489d
   then the returned mantissa is only guaranteed to be <= 1.0, not < 1.0.  */
Packit Service a2489d
Packit Service a2489d
#ifdef USE_LONG_DOUBLE
Packit Service a2489d
# define FUNC frexpl
Packit Service a2489d
# define DOUBLE long double
Packit Service a2489d
# define ISNAN isnanl
Packit Service a2489d
# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING
Packit Service a2489d
# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING ()
Packit Service a2489d
# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING ()
Packit Service a2489d
# define L_(literal) literal##L
Packit Service a2489d
#else
Packit Service a2489d
# define FUNC frexp
Packit Service a2489d
# define DOUBLE double
Packit Service a2489d
# define ISNAN isnand
Packit Service a2489d
# define DECL_ROUNDING
Packit Service a2489d
# define BEGIN_ROUNDING()
Packit Service a2489d
# define END_ROUNDING()
Packit Service a2489d
# define L_(literal) literal
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
DOUBLE
Packit Service a2489d
FUNC (DOUBLE x, int *expptr)
Packit Service a2489d
{
Packit Service a2489d
  int sign;
Packit Service a2489d
  int exponent;
Packit Service a2489d
  DECL_ROUNDING
Packit Service a2489d
Packit Service a2489d
  /* Test for NaN, infinity, and zero.  */
Packit Service a2489d
  if (ISNAN (x) || x + x == x)
Packit Service a2489d
    {
Packit Service a2489d
      *expptr = 0;
Packit Service a2489d
      return x;
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
  sign = 0;
Packit Service a2489d
  if (x < 0)
Packit Service a2489d
    {
Packit Service a2489d
      x = - x;
Packit Service a2489d
      sign = -1;
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
  BEGIN_ROUNDING ();
Packit Service a2489d
Packit Service a2489d
  {
Packit Service a2489d
    /* Since the exponent is an 'int', it fits in 64 bits.  Therefore the
Packit Service a2489d
       loops are executed no more than 64 times.  */
Packit Service a2489d
    DOUBLE pow2[64]; /* pow2[i] = 2^2^i */
Packit Service a2489d
    DOUBLE powh[64]; /* powh[i] = 2^-2^i */
Packit Service a2489d
    int i;
Packit Service a2489d
Packit Service a2489d
    exponent = 0;
Packit Service a2489d
    if (x >= L_(1.0))
Packit Service a2489d
      {
Packit Service a2489d
        /* A positive exponent.  */
Packit Service a2489d
        DOUBLE pow2_i; /* = pow2[i] */
Packit Service a2489d
        DOUBLE powh_i; /* = powh[i] */
Packit Service a2489d
Packit Service a2489d
        /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
Packit Service a2489d
           x * 2^exponent = argument, x >= 1.0.  */
Packit Service a2489d
        for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
Packit Service a2489d
             ;
Packit Service a2489d
             i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
Packit Service a2489d
          {
Packit Service a2489d
            if (x >= pow2_i)
Packit Service a2489d
              {
Packit Service a2489d
                exponent += (1 << i);
Packit Service a2489d
                x *= powh_i;
Packit Service a2489d
              }
Packit Service a2489d
            else
Packit Service a2489d
              break;
Packit Service a2489d
Packit Service a2489d
            pow2[i] = pow2_i;
Packit Service a2489d
            powh[i] = powh_i;
Packit Service a2489d
          }
Packit Service a2489d
        /* Avoid making x too small, as it could become a denormalized
Packit Service a2489d
           number and thus lose precision.  */
Packit Service a2489d
        while (i > 0 && x < pow2[i - 1])
Packit Service a2489d
          {
Packit Service a2489d
            i--;
Packit Service a2489d
            powh_i = powh[i];
Packit Service a2489d
          }
Packit Service a2489d
        exponent += (1 << i);
Packit Service a2489d
        x *= powh_i;
Packit Service a2489d
        /* Here 2^-2^i <= x < 1.0.  */
Packit Service a2489d
      }
Packit Service a2489d
    else
Packit Service a2489d
      {
Packit Service a2489d
        /* A negative or zero exponent.  */
Packit Service a2489d
        DOUBLE pow2_i; /* = pow2[i] */
Packit Service a2489d
        DOUBLE powh_i; /* = powh[i] */
Packit Service a2489d
Packit Service a2489d
        /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
Packit Service a2489d
           x * 2^exponent = argument, x < 1.0.  */
Packit Service a2489d
        for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
Packit Service a2489d
             ;
Packit Service a2489d
             i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
Packit Service a2489d
          {
Packit Service a2489d
            if (x < powh_i)
Packit Service a2489d
              {
Packit Service a2489d
                exponent -= (1 << i);
Packit Service a2489d
                x *= pow2_i;
Packit Service a2489d
              }
Packit Service a2489d
            else
Packit Service a2489d
              break;
Packit Service a2489d
Packit Service a2489d
            pow2[i] = pow2_i;
Packit Service a2489d
            powh[i] = powh_i;
Packit Service a2489d
          }
Packit Service a2489d
        /* Here 2^-2^i <= x < 1.0.  */
Packit Service a2489d
      }
Packit Service a2489d
Packit Service a2489d
    /* Invariants: x * 2^exponent = argument, and 2^-2^i <= x < 1.0.  */
Packit Service a2489d
    while (i > 0)
Packit Service a2489d
      {
Packit Service a2489d
        i--;
Packit Service a2489d
        if (x < powh[i])
Packit Service a2489d
          {
Packit Service a2489d
            exponent -= (1 << i);
Packit Service a2489d
            x *= pow2[i];
Packit Service a2489d
          }
Packit Service a2489d
      }
Packit Service a2489d
    /* Here 0.5 <= x < 1.0.  */
Packit Service a2489d
  }
Packit Service a2489d
Packit Service a2489d
  if (sign < 0)
Packit Service a2489d
    x = - x;
Packit Service a2489d
Packit Service a2489d
  END_ROUNDING ();
Packit Service a2489d
Packit Service a2489d
  *expptr = exponent;
Packit Service a2489d
  return x;
Packit Service a2489d
}