def _apply_function_in_context(cls, f, args, context): """ Apply an MPFR function 'f' to the given arguments 'args', rounding to the given context. Returns a new Mpfr object with precision taken from the current context. """ rounding = context.rounding bf = mpfr.Mpfr_t.__new__(cls) mpfr.mpfr_init2(bf, context.precision) args = (bf,) + args + (rounding,) ternary = f(*args) with _temporary_exponent_bounds(context.emin, context.emax): ternary = mpfr.mpfr_check_range(bf, ternary, rounding) if context.subnormalize: # mpfr_subnormalize doesn't set underflow and # subnormal flags, so we do that ourselves. We choose # to set the underflow flag for *all* cases where the # 'after rounding' result is smaller than the smallest # normal number, even if that result is exact. # if bf is zero but ternary is nonzero, the underflow # flag will already have been set by mpfr_check_range; underflow = ( mpfr.mpfr_number_p(bf) and not mpfr.mpfr_zero_p(bf) and mpfr.mpfr_get_exp(bf) < context.precision - 1 + context.emin) if underflow: mpfr.mpfr_set_underflow() ternary = mpfr.mpfr_subnormalize(bf, ternary, rounding) if ternary: mpfr.mpfr_set_inexflag() return bf
def mpfr_mod(rop, x, y, rnd): """ Given two MPRF numbers x and y, compute x - floor(x / y) * y, rounded if necessary using the given rounding mode. The result is placed in 'rop'. This is the 'remainder' operation, with sign convention compatible with Python's % operator (where x % y has the same sign as y). """ # There are various cases: # # 0. If either argument is a NaN, the result is NaN. # # 1. If x is infinite or y is zero, the result is NaN. # # 2. If y is infinite, return 0 with the sign of y if x is zero, x if x has # the same sign as y, and infinity with the sign of y if it has the # opposite sign. # # 3. If none of the above cases apply then both x and y are finite, # and y is nonzero. If x and y have the same sign, simply # return the result of fmod(x, y). # # 4. Now both x and y are finite, y is nonzero, and x and y have # differing signs. Compute r = fmod(x, y) with sufficient precision # to get an exact result. If r == 0, return 0 with the sign of y # (which will be the opposite of the sign of x). If r != 0, # return r + y, rounded appropriately. if not mpfr.mpfr_number_p(x) or mpfr.mpfr_nan_p(y) or mpfr.mpfr_zero_p(y): return mpfr.mpfr_fmod(rop, x, y, rnd) elif mpfr.mpfr_inf_p(y): x_negative = mpfr.mpfr_signbit(x) y_negative = mpfr.mpfr_signbit(y) if mpfr.mpfr_zero_p(x): mpfr.mpfr_set_zero(rop, -y_negative) return 0 elif x_negative == y_negative: return mpfr.mpfr_set(rop, x, rnd) else: mpfr.mpfr_set_inf(rop, -y_negative) return 0 x_negative = mpfr.mpfr_signbit(x) y_negative = mpfr.mpfr_signbit(y) if x_negative == y_negative: return mpfr.mpfr_fmod(rop, x, y, rnd) else: p = max(mpfr.mpfr_get_prec(x), mpfr.mpfr_get_prec(y)) z = mpfr.Mpfr_t() mpfr.mpfr_init2(z, p) # Doesn't matter what rounding mode we use here; the result # should be exact. ternary = mpfr.mpfr_fmod(z, x, y, rnd) assert ternary == 0 if mpfr.mpfr_zero_p(z): mpfr.mpfr_set_zero(rop, -y_negative) return 0 else: return mpfr.mpfr_add(rop, y, z, rnd)