mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
177 lines
4.8 KiB
C
177 lines
4.8 KiB
C
/*=============================================================================
|
|
|
|
This file is part of ARB.
|
|
|
|
ARB is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ARB 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with ARB; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
=============================================================================*/
|
|
/******************************************************************************
|
|
|
|
Copyright (C) 2013 Fredrik Johansson
|
|
|
|
******************************************************************************/
|
|
|
|
#include "fmpr.h"
|
|
|
|
/* like mpn_scan0, but takes an upper size */
|
|
static __inline__ mp_bitcnt_t
|
|
mpn_scan0b(mp_srcptr up, mp_size_t size, mp_bitcnt_t from_bit)
|
|
{
|
|
mp_limb_t t;
|
|
long i, c;
|
|
|
|
i = from_bit / GMP_NUMB_BITS;
|
|
c = from_bit % FLINT_BITS;
|
|
t = ((~up[i]) >> c) << c;
|
|
|
|
while (t == 0)
|
|
{
|
|
i++;
|
|
if (i == size)
|
|
return size * FLINT_BITS;
|
|
else
|
|
t = ~up[i];
|
|
}
|
|
|
|
count_trailing_zeros(c, t);
|
|
return (i * FLINT_BITS) + c;
|
|
}
|
|
|
|
long
|
|
_fmpr_set_round_mpn(long * shift, fmpz_t man, mp_srcptr x, mp_size_t xn, int negative, long prec, fmpr_rnd_t rnd)
|
|
{
|
|
long bc, val, val_bits, val_limbs, ret;
|
|
int increment;
|
|
|
|
/* compute the total bit length of x */
|
|
count_leading_zeros(bc, x[xn - 1]);
|
|
bc = FLINT_BITS - bc;
|
|
bc += (xn - 1) * FLINT_BITS;
|
|
|
|
/* already odd */
|
|
if (x[0] & 1)
|
|
{
|
|
/* quick exit */
|
|
if (bc <= prec)
|
|
{
|
|
if (bc <= FLINT_BITS - 2)
|
|
{
|
|
mp_limb_t t = x[0];
|
|
_fmpz_demote(man);
|
|
*man = negative ? -t : t;
|
|
}
|
|
else
|
|
fmpz_set_mpn_large(man, x, xn, negative);
|
|
|
|
*shift = 0;
|
|
return FMPR_RESULT_EXACT;
|
|
}
|
|
else
|
|
{
|
|
val_limbs = val_bits = val = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* trailing zero bits: val = val_limbs * FLINT_BITS + val_bits */
|
|
val_limbs = 0;
|
|
while (x[val_limbs] == 0)
|
|
val_limbs++;
|
|
count_trailing_zeros(val_bits, x[val_limbs]);
|
|
val = val_bits + (val_limbs * FLINT_BITS);
|
|
}
|
|
|
|
/* no rounding necessary; just copy or shift to destination */
|
|
if (bc - val <= prec)
|
|
{
|
|
ret = FMPR_RESULT_EXACT;
|
|
increment = 0;
|
|
}
|
|
else
|
|
{
|
|
/* truncation */
|
|
if (!rounds_up(rnd, negative))
|
|
{
|
|
val = mpn_scan1(x, bc - prec);
|
|
increment = 0;
|
|
}
|
|
/* round to next higher odd mantissa */
|
|
else
|
|
{
|
|
val = mpn_scan0b(x, xn, bc - prec);
|
|
|
|
/* can overflow to next power of 2 */
|
|
if (val == bc)
|
|
{
|
|
fmpz_set_si(man, negative ? -1 : 1);
|
|
*shift = bc;
|
|
return prec;
|
|
}
|
|
|
|
/* otherwise, we are cutting off at a zero bit, and
|
|
incrementing at that position will not cause carry
|
|
propagation below */
|
|
increment = 1;
|
|
}
|
|
|
|
val_limbs = val / FLINT_BITS;
|
|
val_bits = val % FLINT_BITS;
|
|
ret = prec - (bc - val);
|
|
}
|
|
|
|
/* the output mantissa is a small fmpz */
|
|
if (bc - val <= FLINT_BITS - 2)
|
|
{
|
|
mp_limb_t h;
|
|
|
|
if (val_limbs + 1 == xn || val_bits == 0)
|
|
h = x[val_limbs] >> val_bits;
|
|
else
|
|
h = (x[val_limbs] >> val_bits) | (x[val_limbs + 1] << (FLINT_BITS - val_bits));
|
|
|
|
h += increment;
|
|
_fmpz_demote(man);
|
|
*man = negative ? -h : h;
|
|
}
|
|
/* the output mantissa is an mpz */
|
|
else
|
|
{
|
|
mp_ptr dest;
|
|
long res_limbs, res_alloc;
|
|
__mpz_struct * zptr = _fmpz_promote(man);
|
|
|
|
res_limbs = ((bc - val) + FLINT_BITS - 1) / FLINT_BITS;
|
|
/* todo: only allocate the required size, not the size before shifting */
|
|
res_alloc = xn - val_limbs;
|
|
|
|
if (zptr->_mp_alloc < res_alloc)
|
|
mpz_realloc2(zptr, res_alloc * FLINT_BITS);
|
|
|
|
dest = zptr->_mp_d;
|
|
|
|
/* right shift by val */
|
|
if (val_bits == 0)
|
|
flint_mpn_copyi(dest, x + val_limbs, res_limbs);
|
|
else
|
|
mpn_rshift(dest, x + val_limbs, xn - val_limbs, val_bits);
|
|
|
|
dest[0] += increment;
|
|
zptr->_mp_size = negative ? -res_limbs : res_limbs;
|
|
}
|
|
|
|
*shift = val;
|
|
return ret;
|
|
}
|
|
|