/* Copyright (C) 2014 Fredrik Johansson This file is part of Arb. Arb is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include "arf.h" /* Top-aligns the single-limb integer value v and rounds it to prec bits. Writes inexact, v, exp. Warning: macro without parentheses. */ #define ARF_NORMALISE_ROUND_LIMB(inexact, exp, v, sgnbit, prec, rnd) \ do { \ count_leading_zeros(exp, v); \ v <<= exp; \ exp = FLINT_BITS - exp; \ if (prec >= exp) \ { \ inexact = 0; \ } \ else \ { \ mp_limb_t hi_mask, lo_mask, rndn_mask, __t, __u; \ hi_mask = LIMB_ONES << (FLINT_BITS - prec); \ __t = v & hi_mask; \ inexact = (__t != v); \ if (inexact && rnd != ARF_RND_DOWN) \ { \ if (rnd == ARF_RND_NEAR) \ { \ lo_mask = LIMB_ONES >> prec; \ rndn_mask = LIMB_ONE << (FLINT_BITS - prec - 1); \ __u = v & lo_mask; \ if (__u > rndn_mask || (__u == rndn_mask && \ (__t << (prec - 1)))) \ __t += (LIMB_ONE << (FLINT_BITS - prec)); \ } \ else if (arf_rounds_up(rnd, sgnbit)) \ { \ __t += (LIMB_ONE << (FLINT_BITS - prec)); \ } \ if (__t == 0) \ { \ __t = LIMB_TOP; \ exp++; \ } \ } \ v = __t; \ } \ } while (0) int _arf_set_round_uiui(arf_t z, slong * fix, mp_limb_t hi, mp_limb_t lo, int sgnbit, slong prec, arf_rnd_t rnd) { int leading, trailing, bc, inexact, zn, up, exp; if (hi == 0) { ARF_NORMALISE_ROUND_LIMB(inexact, exp, lo, sgnbit, prec, rnd); leading = 2 * FLINT_BITS - exp; zn = 1; } else if (lo == 0) { ARF_NORMALISE_ROUND_LIMB(inexact, exp, hi, sgnbit, prec, rnd); leading = FLINT_BITS - exp; lo = hi; zn = 1; } else { count_leading_zeros(leading, hi); count_trailing_zeros(trailing, lo); bc = 2 * FLINT_BITS - leading - trailing; if (bc <= prec) { inexact = 0; zn = 2; if (leading != 0) { if (bc <= FLINT_BITS) { lo = (hi << leading) | (lo >> (FLINT_BITS - leading)); zn = 1; } else { hi = (hi << leading) | (lo >> (FLINT_BITS - leading)); lo = (lo << leading); } } } else { inexact = 1; if (rnd == ARF_RND_DOWN) { up = 0; } else if (rnd == ARF_RND_NEAR) { if (bc == prec + 1) { /* exactly one excess bit; check the parity bit which must be either the lsb of hi or a bit in lo */ if (trailing == FLINT_BITS - 1) up = hi & 1; else up = (lo >> (trailing + 1)) & 1; } else { /* two or more excess bits; test the first excess bit */ flint_bitcnt_t pos = 2 * FLINT_BITS - leading - prec - 1; if (pos < FLINT_BITS) up = (lo >> pos) & 1; else up = (hi >> (pos - FLINT_BITS)) & 1; } } else { up = arf_rounds_up(rnd, sgnbit); } if (prec <= FLINT_BITS) { zn = 1; if (leading == 0) lo = hi; else lo = (hi << leading) | (lo >> (FLINT_BITS - leading)); lo = lo & (LIMB_ONES << (FLINT_BITS - prec)); if (up) { mp_limb_t t, ovf; t = lo + (LIMB_ONE << (FLINT_BITS - prec)); ovf = (t == 0); leading -= ovf; lo = (t >> ovf) | (ovf << (FLINT_BITS - 1)); } } else { zn = 2; if (leading != 0) { hi = (hi << leading) | (lo >> (FLINT_BITS - leading)); lo = (lo << leading); } lo = lo & (LIMB_ONES << (2 * FLINT_BITS - prec)); if (up) { add_ssaaaa(hi, lo, hi, lo, 0, (LIMB_ONE << (2 * FLINT_BITS - prec))); } if (lo == 0) { if (hi == 0) { leading -= 1; lo = LIMB_TOP; } else { lo = hi; } zn = 1; } } } } *fix = -leading; ARF_DEMOTE(z); ARF_XSIZE(z) = ARF_MAKE_XSIZE(zn, sgnbit); ARF_NOPTR_D(z)[0] = lo; ARF_NOPTR_D(z)[1] = hi; return inexact; }