|
Packit |
5c3484 |
/* mpf_get_str (digit_ptr, exp, base, n_digits, a) -- Convert the floating
|
|
Packit |
5c3484 |
point number A to a base BASE number and store N_DIGITS raw digits at
|
|
Packit |
5c3484 |
DIGIT_PTR, and the base BASE exponent in the word pointed to by EXP. For
|
|
Packit |
5c3484 |
example, the number 3.1416 would be returned as "31416" in DIGIT_PTR and
|
|
Packit |
5c3484 |
1 in EXP.
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
Copyright 1993-1997, 2000-2003, 2005, 2006, 2011, 2015 Free Software
|
|
Packit |
5c3484 |
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 <stdlib.h> /* for NULL */
|
|
Packit |
5c3484 |
#include "gmp.h"
|
|
Packit |
5c3484 |
#include "gmp-impl.h"
|
|
Packit |
5c3484 |
#include "longlong.h" /* for count_leading_zeros */
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* Could use some more work.
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
1. Allocation is excessive. Try to combine areas. Perhaps use result
|
|
Packit |
5c3484 |
string area for temp limb space?
|
|
Packit |
5c3484 |
2. We generate up to two limbs of extra digits. This is because we don't
|
|
Packit |
5c3484 |
check the exact number of bits in the input operand, and from that
|
|
Packit |
5c3484 |
compute an accurate exponent (variable e in the code). It would be
|
|
Packit |
5c3484 |
cleaner and probably somewhat faster to change this.
|
|
Packit |
5c3484 |
*/
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* Compute base^exp and return the most significant prec limbs in rp[].
|
|
Packit |
5c3484 |
Put the count of omitted low limbs in *ign.
|
|
Packit |
5c3484 |
Return the actual size (which might be less than prec).
|
|
Packit |
5c3484 |
Allocation of rp[] and the temporary tp[] should be 2*prec+2 limbs. */
|
|
Packit |
5c3484 |
static mp_size_t
|
|
Packit |
5c3484 |
mpn_pow_1_highpart (mp_ptr rp, mp_size_t *ignp,
|
|
Packit |
5c3484 |
mp_limb_t base, unsigned long exp,
|
|
Packit |
5c3484 |
mp_size_t prec, mp_ptr tp)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
mp_size_t ign; /* counts number of ignored low limbs in r */
|
|
Packit |
5c3484 |
mp_size_t off; /* keeps track of offset where value starts */
|
|
Packit |
5c3484 |
mp_ptr passed_rp = rp;
|
|
Packit |
5c3484 |
mp_size_t rn;
|
|
Packit |
5c3484 |
int cnt;
|
|
Packit |
5c3484 |
int i;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (exp == 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
rp[0] = 1;
|
|
Packit |
5c3484 |
*ignp = 0;
|
|
Packit |
5c3484 |
return 1;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
rp[0] = base;
|
|
Packit |
5c3484 |
rn = 1;
|
|
Packit |
5c3484 |
off = 0;
|
|
Packit |
5c3484 |
ign = 0;
|
|
Packit |
5c3484 |
count_leading_zeros (cnt, exp);
|
|
Packit |
5c3484 |
for (i = GMP_LIMB_BITS - cnt - 2; i >= 0; i--)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
mpn_sqr (tp, rp + off, rn);
|
|
Packit |
5c3484 |
rn = 2 * rn;
|
|
Packit |
5c3484 |
rn -= tp[rn - 1] == 0;
|
|
Packit |
5c3484 |
ign <<= 1;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
off = 0;
|
|
Packit |
5c3484 |
if (rn > prec)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
ign += rn - prec;
|
|
Packit |
5c3484 |
off = rn - prec;
|
|
Packit |
5c3484 |
rn = prec;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
MP_PTR_SWAP (rp, tp);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (((exp >> i) & 1) != 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
mp_limb_t cy;
|
|
Packit |
5c3484 |
cy = mpn_mul_1 (rp, rp + off, rn, base);
|
|
Packit |
5c3484 |
rp[rn] = cy;
|
|
Packit |
5c3484 |
rn += cy != 0;
|
|
Packit |
5c3484 |
off = 0;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (rn > prec)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
ASSERT (rn == prec + 1);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
ign += rn - prec;
|
|
Packit |
5c3484 |
rp += rn - prec;
|
|
Packit |
5c3484 |
rn = prec;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* With somewhat less than 50% probability, we can skip this copy. */
|
|
Packit |
5c3484 |
if (passed_rp != rp + off)
|
|
Packit |
5c3484 |
MPN_COPY_INCR (passed_rp, rp + off, rn);
|
|
Packit |
5c3484 |
*ignp = ign;
|
|
Packit |
5c3484 |
return rn;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
char *
|
|
Packit |
5c3484 |
mpf_get_str (char *dbuf, mp_exp_t *exp, int base, size_t n_digits, mpf_srcptr u)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
mp_exp_t ue;
|
|
Packit |
5c3484 |
mp_size_t n_limbs_needed;
|
|
Packit |
5c3484 |
size_t max_digits;
|
|
Packit |
5c3484 |
mp_ptr up, pp, tp;
|
|
Packit |
5c3484 |
mp_size_t un, pn, tn;
|
|
Packit |
5c3484 |
unsigned char *tstr;
|
|
Packit |
5c3484 |
mp_exp_t exp_in_base;
|
|
Packit |
5c3484 |
size_t n_digits_computed;
|
|
Packit |
5c3484 |
mp_size_t i;
|
|
Packit |
5c3484 |
const char *num_to_text;
|
|
Packit |
5c3484 |
size_t alloc_size = 0;
|
|
Packit |
5c3484 |
char *dp;
|
|
Packit |
5c3484 |
TMP_DECL;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
up = PTR(u);
|
|
Packit |
5c3484 |
un = ABSIZ(u);
|
|
Packit |
5c3484 |
ue = EXP(u);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (base >= 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
num_to_text = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
Packit |
5c3484 |
if (base <= 1)
|
|
Packit |
5c3484 |
base = 10;
|
|
Packit |
5c3484 |
else if (base > 36)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
Packit |
5c3484 |
if (base > 62)
|
|
Packit |
5c3484 |
return NULL;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
else
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
base = -base;
|
|
Packit |
5c3484 |
if (base <= 1)
|
|
Packit |
5c3484 |
base = 10;
|
|
Packit |
5c3484 |
else if (base > 36)
|
|
Packit |
5c3484 |
return NULL;
|
|
Packit |
5c3484 |
num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
MPF_SIGNIFICANT_DIGITS (max_digits, base, PREC(u));
|
|
Packit |
5c3484 |
if (n_digits == 0 || n_digits > max_digits)
|
|
Packit |
5c3484 |
n_digits = max_digits;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (dbuf == 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
/* We didn't get a string from the user. Allocate one (and return
|
|
Packit |
5c3484 |
a pointer to it) with space for `-' and terminating null. */
|
|
Packit |
5c3484 |
alloc_size = n_digits + 2;
|
|
Packit |
5c3484 |
dbuf = (char *) (*__gmp_allocate_func) (n_digits + 2);
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (un == 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
*exp = 0;
|
|
Packit |
5c3484 |
*dbuf = 0;
|
|
Packit |
5c3484 |
n_digits = 0;
|
|
Packit |
5c3484 |
goto done;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
TMP_MARK;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* Allocate temporary digit space. We can't put digits directly in the user
|
|
Packit |
5c3484 |
area, since we generate more digits than requested. (We allocate
|
|
Packit |
5c3484 |
2 * GMP_LIMB_BITS extra bytes because of the digit block nature of the
|
|
Packit |
5c3484 |
conversion.) */
|
|
Packit |
5c3484 |
tstr = (unsigned char *) TMP_ALLOC (n_digits + 2 * GMP_LIMB_BITS + 3);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
LIMBS_PER_DIGIT_IN_BASE (n_limbs_needed, n_digits, base);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (un > n_limbs_needed)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
up += un - n_limbs_needed;
|
|
Packit |
5c3484 |
un = n_limbs_needed;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
TMP_ALLOC_LIMBS_2 (pp, 2 * n_limbs_needed + 4,
|
|
Packit |
5c3484 |
tp, 2 * n_limbs_needed + 4);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (ue <= n_limbs_needed)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
/* We need to multiply number by base^n to get an n_digits integer part. */
|
|
Packit |
5c3484 |
mp_size_t n_more_limbs_needed, ign, off;
|
|
Packit |
5c3484 |
unsigned long e;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
n_more_limbs_needed = n_limbs_needed - ue;
|
|
Packit |
5c3484 |
DIGITS_IN_BASE_PER_LIMB (e, n_more_limbs_needed, base);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
|
|
Packit |
5c3484 |
if (un > pn)
|
|
Packit |
5c3484 |
mpn_mul (tp, up, un, pp, pn); /* FIXME: mpn_mul_highpart */
|
|
Packit |
5c3484 |
else
|
|
Packit |
5c3484 |
mpn_mul (tp, pp, pn, up, un); /* FIXME: mpn_mul_highpart */
|
|
Packit |
5c3484 |
tn = un + pn;
|
|
Packit |
5c3484 |
tn -= tp[tn - 1] == 0;
|
|
Packit |
5c3484 |
off = un - ue - ign;
|
|
Packit |
5c3484 |
if (off < 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
MPN_COPY_DECR (tp - off, tp, tn);
|
|
Packit |
5c3484 |
MPN_ZERO (tp, -off);
|
|
Packit |
5c3484 |
tn -= off;
|
|
Packit |
5c3484 |
off = 0;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
n_digits_computed = mpn_get_str (tstr, base, tp + off, tn - off);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
exp_in_base = n_digits_computed - e;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
else
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
/* We need to divide number by base^n to get an n_digits integer part. */
|
|
Packit |
5c3484 |
mp_size_t n_less_limbs_needed, ign, off, xn;
|
|
Packit |
5c3484 |
unsigned long e;
|
|
Packit |
5c3484 |
mp_ptr dummyp, xp;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
n_less_limbs_needed = ue - n_limbs_needed;
|
|
Packit |
5c3484 |
DIGITS_IN_BASE_PER_LIMB (e, n_less_limbs_needed, base);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
xn = n_limbs_needed + (n_less_limbs_needed-ign);
|
|
Packit |
5c3484 |
xp = TMP_ALLOC_LIMBS (xn);
|
|
Packit |
5c3484 |
off = xn - un;
|
|
Packit |
5c3484 |
MPN_ZERO (xp, off);
|
|
Packit |
5c3484 |
MPN_COPY (xp + off, up, un);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
dummyp = TMP_ALLOC_LIMBS (pn);
|
|
Packit |
5c3484 |
mpn_tdiv_qr (tp, dummyp, (mp_size_t) 0, xp, xn, pp, pn);
|
|
Packit |
5c3484 |
tn = xn - pn + 1;
|
|
Packit |
5c3484 |
tn -= tp[tn - 1] == 0;
|
|
Packit |
5c3484 |
n_digits_computed = mpn_get_str (tstr, base, tp, tn);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
exp_in_base = n_digits_computed + e;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* We should normally have computed too many digits. Round the result
|
|
Packit |
5c3484 |
at the point indicated by n_digits. */
|
|
Packit |
5c3484 |
if (n_digits_computed > n_digits)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
size_t i;
|
|
Packit |
5c3484 |
/* Round the result. */
|
|
Packit |
5c3484 |
if (tstr[n_digits] * 2 >= base)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
n_digits_computed = n_digits;
|
|
Packit |
5c3484 |
for (i = n_digits - 1;; i--)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
unsigned int x;
|
|
Packit |
5c3484 |
x = ++(tstr[i]);
|
|
Packit |
5c3484 |
if (x != base)
|
|
Packit |
5c3484 |
break;
|
|
Packit |
5c3484 |
n_digits_computed--;
|
|
Packit |
5c3484 |
if (i == 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
/* We had something like `bbbbbbb...bd', where 2*d >= base
|
|
Packit |
5c3484 |
and `b' denotes digit with significance base - 1.
|
|
Packit |
5c3484 |
This rounds up to `1', increasing the exponent. */
|
|
Packit |
5c3484 |
tstr[0] = 1;
|
|
Packit |
5c3484 |
n_digits_computed = 1;
|
|
Packit |
5c3484 |
exp_in_base++;
|
|
Packit |
5c3484 |
break;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* We might have fewer digits than requested as a result of rounding above,
|
|
Packit |
5c3484 |
(i.e. 0.999999 => 1.0) or because we have a number that simply doesn't
|
|
Packit |
5c3484 |
need many digits in this base (e.g., 0.125 in base 10). */
|
|
Packit |
5c3484 |
if (n_digits > n_digits_computed)
|
|
Packit |
5c3484 |
n_digits = n_digits_computed;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* Remove trailing 0. There can be many zeros. */
|
|
Packit |
5c3484 |
while (n_digits != 0 && tstr[n_digits - 1] == 0)
|
|
Packit |
5c3484 |
n_digits--;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
dp = dbuf + (SIZ(u) < 0);
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
/* Translate to ASCII and copy to result string. */
|
|
Packit |
5c3484 |
for (i = 0; i < n_digits; i++)
|
|
Packit |
5c3484 |
dp[i] = num_to_text[tstr[i]];
|
|
Packit |
5c3484 |
dp[n_digits] = 0;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
*exp = exp_in_base;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
if (SIZ(u) < 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
dbuf[0] = '-';
|
|
Packit |
5c3484 |
n_digits++;
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
TMP_FREE;
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
done:
|
|
Packit |
5c3484 |
/* If the string was alloced then resize it down to the actual space
|
|
Packit |
5c3484 |
required. */
|
|
Packit |
5c3484 |
if (alloc_size != 0)
|
|
Packit |
5c3484 |
{
|
|
Packit |
5c3484 |
__GMP_REALLOCATE_FUNC_MAYBE_TYPE (dbuf, alloc_size, n_digits + 1, char);
|
|
Packit |
5c3484 |
}
|
|
Packit |
5c3484 |
|
|
Packit |
5c3484 |
return dbuf;
|
|
Packit |
5c3484 |
}
|