Blame mpq/get_d.c

Packit 5c3484
/* double mpq_get_d (mpq_t src) -- mpq to double, rounding towards zero.
Packit 5c3484
Packit 5c3484
Copyright 1995, 1996, 2001-2005 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include <stdio.h>  /* for NULL */
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* All that's needed is to get the high 53 bits of the quotient num/den,
Packit 5c3484
   rounded towards zero.  More than 53 bits is fine, any excess is ignored
Packit 5c3484
   by mpn_get_d.
Packit 5c3484
Packit 5c3484
   N_QLIMBS is how many quotient limbs we need to satisfy the mantissa of a
Packit 5c3484
   double, assuming the highest of those limbs is non-zero.  The target
Packit 5c3484
   qsize for mpn_tdiv_qr is then 1 more than this, since that function may
Packit 5c3484
   give a zero in the high limb (and non-zero in the second highest).
Packit 5c3484
Packit 5c3484
   The use of 8*sizeof(double) in N_QLIMBS is an overestimate of the
Packit 5c3484
   mantissa bits, but it gets the same result as the true value (53 or 48 or
Packit 5c3484
   whatever) when rounded up to a multiple of GMP_NUMB_BITS, for non-nails.
Packit 5c3484
Packit 5c3484
   Enhancements:
Packit 5c3484
Packit 5c3484
   Use the true mantissa size in the N_QLIMBS formula, to save a divide step
Packit 5c3484
   in nails.
Packit 5c3484
Packit 5c3484
   Examine the high limbs of num and den to see if the highest 1 bit of the
Packit 5c3484
   quotient will fall high enough that just N_QLIMBS-1 limbs is enough to
Packit 5c3484
   get the necessary bits, thereby saving a division step.
Packit 5c3484
Packit 5c3484
   Bit shift either num or den to arrange for the above condition on the
Packit 5c3484
   high 1 bit of the quotient, to save a division step always.  A shift to
Packit 5c3484
   save a division step is definitely worthwhile with mpn_tdiv_qr, though we
Packit 5c3484
   may want to reassess this on big num/den when a quotient-only division
Packit 5c3484
   exists.
Packit 5c3484
Packit 5c3484
   Maybe we could estimate the final exponent using nsize-dsize (and
Packit 5c3484
   possibly the high limbs of num and den), so as to detect overflow and
Packit 5c3484
   return infinity or zero quickly.  Overflow is never very helpful to an
Packit 5c3484
   application, and can therefore probably be regarded as abnormal, but we
Packit 5c3484
   may still like to optimize it if the conditions are easy.  (This would
Packit 5c3484
   only be for float formats we know, unknown formats are not important and
Packit 5c3484
   can be left to mpn_get_d.)
Packit 5c3484
Packit 5c3484
   Future:
Packit 5c3484
Packit 5c3484
   If/when mpn_tdiv_qr supports its qxn parameter we can use that instead of
Packit 5c3484
   padding n with zeros in temporary space.
Packit 5c3484
Packit 5c3484
   If/when a quotient-only division exists it can be used here immediately.
Packit 5c3484
   remp is only to satisfy mpn_tdiv_qr, the remainder is not used.
Packit 5c3484
Packit 5c3484
   Alternatives:
Packit 5c3484
Packit 5c3484
   An alternative algorithm, that may be faster:
Packit 5c3484
   0. Let n be somewhat larger than the number of significant bits in a double.
Packit 5c3484
   1. Extract the most significant n bits of the denominator, and an equal
Packit 5c3484
      number of bits from the numerator.
Packit 5c3484
   2. Interpret the extracted numbers as integers, call them a and b
Packit 5c3484
      respectively, and develop n bits of the fractions ((a + 1) / b) and
Packit 5c3484
      (a / (b + 1)) using mpn_divrem.
Packit 5c3484
   3. If the computed values are identical UP TO THE POSITION WE CARE ABOUT,
Packit 5c3484
      we are done.  If they are different, repeat the algorithm from step 1,
Packit 5c3484
      but first let n = n * 2.
Packit 5c3484
   4. If we end up using all bits from the numerator and denominator, fall
Packit 5c3484
      back to a plain division.
Packit 5c3484
   5. Just to make life harder, The computation of a + 1 and b + 1 above
Packit 5c3484
      might give carry-out...  Needs special handling.  It might work to
Packit 5c3484
      subtract 1 in both cases instead.
Packit 5c3484
Packit 5c3484
   Not certain if this approach would be faster than a quotient-only
Packit 5c3484
   division.  Presumably such optimizations are the sort of thing we would
Packit 5c3484
   like to have helping everywhere that uses a quotient-only division. */
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
mpq_get_d (mpq_srcptr src)
Packit 5c3484
{
Packit 5c3484
  double res;
Packit 5c3484
  mp_srcptr np, dp;
Packit 5c3484
  mp_ptr remp, tp;
Packit 5c3484
  mp_size_t nsize = SIZ(NUM(src));
Packit 5c3484
  mp_size_t dsize = SIZ(DEN(src));
Packit 5c3484
  mp_size_t qsize, prospective_qsize, zeros, chop, tsize;
Packit 5c3484
  mp_size_t sign_quotient = nsize;
Packit 5c3484
  long exp;
Packit 5c3484
#define N_QLIMBS (1 + (sizeof (double) + GMP_LIMB_BYTES-1) / GMP_LIMB_BYTES)
Packit 5c3484
  mp_limb_t qarr[N_QLIMBS + 1];
Packit 5c3484
  mp_ptr qp = qarr;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
Packit 5c3484
  ASSERT (dsize > 0);    /* canonical src */
Packit 5c3484
Packit 5c3484
  /* mpn_get_d below requires a non-zero operand */
Packit 5c3484
  if (UNLIKELY (nsize == 0))
Packit 5c3484
    return 0.0;
Packit 5c3484
Packit 5c3484
  TMP_MARK;
Packit 5c3484
  nsize = ABS (nsize);
Packit 5c3484
  dsize = ABS (dsize);
Packit 5c3484
  np = PTR(NUM(src));
Packit 5c3484
  dp = PTR(DEN(src));
Packit 5c3484
Packit 5c3484
  prospective_qsize = nsize - dsize + 1;   /* from using given n,d */
Packit 5c3484
  qsize = N_QLIMBS + 1;                    /* desired qsize */
Packit 5c3484
Packit 5c3484
  zeros = qsize - prospective_qsize;       /* padding n to get qsize */
Packit 5c3484
  exp = (long) -zeros * GMP_NUMB_BITS;     /* relative to low of qp */
Packit 5c3484
Packit 5c3484
  chop = MAX (-zeros, 0);                  /* negative zeros means shorten n */
Packit 5c3484
  np += chop;
Packit 5c3484
  nsize -= chop;
Packit 5c3484
  zeros += chop;                           /* now zeros >= 0 */
Packit 5c3484
Packit 5c3484
  tsize = nsize + zeros;                   /* size for possible copy of n */
Packit 5c3484
Packit 5c3484
  if (WANT_TMP_DEBUG)
Packit 5c3484
    {
Packit 5c3484
      /* separate blocks, for malloc debugging */
Packit 5c3484
      remp = TMP_ALLOC_LIMBS (dsize);
Packit 5c3484
      tp = (zeros > 0 ? TMP_ALLOC_LIMBS (tsize) : NULL);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* one block with conditionalized size, for efficiency */
Packit 5c3484
      remp = TMP_ALLOC_LIMBS (dsize + (zeros > 0 ? tsize : 0));
Packit 5c3484
      tp = remp + dsize;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* zero extend n into temporary space, if necessary */
Packit 5c3484
  if (zeros > 0)
Packit 5c3484
    {
Packit 5c3484
      MPN_ZERO (tp, zeros);
Packit 5c3484
      MPN_COPY (tp+zeros, np, nsize);
Packit 5c3484
      np = tp;
Packit 5c3484
      nsize = tsize;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  ASSERT (qsize == nsize - dsize + 1);
Packit 5c3484
  mpn_tdiv_qr (qp, remp, (mp_size_t) 0, np, nsize, dp, dsize);
Packit 5c3484
Packit 5c3484
  /* strip possible zero high limb */
Packit 5c3484
  qsize -= (qp[qsize-1] == 0);
Packit 5c3484
Packit 5c3484
  res = mpn_get_d (qp, qsize, sign_quotient, exp);
Packit 5c3484
  TMP_FREE;
Packit 5c3484
  return res;
Packit 5c3484
}