Support rounding property for the mp context
This commit is contained in:
@@ -44,7 +44,7 @@ Features:
|
||||
* Support 'b' (binary) format type for mpf/mpc, see #867 (Sergey B Kirpichev)
|
||||
* Implement mpf.__floordiv__() and mpf.__divmod__(), see #873 (Sergey B
|
||||
Kirpichev)
|
||||
* Add parameters for MPContext constructor, see #876 (Sergey B Kirpichev)
|
||||
* Add parameters for MPContext constructor, see #876 and #963 (Sergey B Kirpichev)
|
||||
* Add MPFR-compatible aliases for rounding modes, see #892 (Sergey B
|
||||
Kirpichev)
|
||||
* Support negative indexes in matrix, see #897 (Riccardo Orsi)
|
||||
@@ -59,6 +59,7 @@ Features:
|
||||
* Use PyREPL, as fallback (no IPython), see #941 (Sergey B Kirpichev)
|
||||
* Add exp2() and log2(), see #948 (Sergey B Kirpichev)
|
||||
* Add isspecial() method for contexts, see #949 (Sergey B Kirpichev)
|
||||
* Support rounding property for the mp context, see #963 (Sergey B Kirpichev)
|
||||
|
||||
Compatibility:
|
||||
|
||||
|
||||
+3
-2
@@ -24,7 +24,8 @@ def pytest_configure(config):
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_mp_globals():
|
||||
from mpmath import mp, iv
|
||||
mp.dps = 15
|
||||
mp.prec = sys.float_info.mant_dig
|
||||
mp.pretty = False
|
||||
iv.dps = 15
|
||||
mp.rounding = 'n'
|
||||
iv.prec = mp.prec
|
||||
iv.pretty = False
|
||||
|
||||
@@ -80,6 +80,7 @@ Mpmath uses a global working precision; it does not keep track of the precision
|
||||
Mpmath settings:
|
||||
mp.prec = 53 [default: 53]
|
||||
mp.dps = 15 [default: 15]
|
||||
mp.rounding = 'n' [default: 'n']
|
||||
mp.trap_complex = False [default: False]
|
||||
|
||||
The term **prec** denotes the binary precision (measured in bits) while **dps** (short for *decimal places*) is the decimal precision. Binary and decimal precision are related roughly according to the formula ``prec = 3.33*dps``. For example, it takes a precision of roughly 333 bits to hold an approximation of pi that is accurate to 100 decimal places (actually slightly more than 333 bits is used).
|
||||
@@ -120,6 +121,17 @@ Or why not 1 googolplex:
|
||||
|
||||
The (binary) exponent is stored exactly and is independent of the precision.
|
||||
|
||||
The ``rounding`` property control default rounding mode for the context:
|
||||
|
||||
>>> mp.rounding # round to nearest
|
||||
'n'
|
||||
>>> sin(1)
|
||||
mpf('0.8414709848078965')
|
||||
>>> mp.rounding = 'u' # round up
|
||||
>>> sin(1)
|
||||
mpf('0.84147098480789662')
|
||||
>>> mp.rounding = 'n'
|
||||
|
||||
Temporarily changing the precision
|
||||
..................................
|
||||
|
||||
|
||||
+5
-2
@@ -18,7 +18,7 @@ from .libmp import (MPQ, MPZ_ONE, ComplexResult, dps_to_prec, finf, fnan,
|
||||
mpf_degree, mpf_div, mpf_e, mpf_euler, mpf_glaisher,
|
||||
mpf_khinchin, mpf_ln2, mpf_ln10, mpf_mertens, mpf_mul,
|
||||
mpf_neg, mpf_phi, mpf_pi, mpf_rand, mpf_sub, mpf_twinprime,
|
||||
repr_dps, to_man_exp, to_str)
|
||||
repr_dps, to_man_exp, to_str, round_nearest)
|
||||
|
||||
|
||||
get_complex = re.compile(r"""
|
||||
@@ -44,12 +44,14 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
Context for multiprecision arithmetic with a global precision.
|
||||
"""
|
||||
|
||||
def __init__(ctx, prec=sys.float_info.mant_dig, trap_complex=False):
|
||||
def __init__(ctx, prec=sys.float_info.mant_dig,
|
||||
rounding=round_nearest, trap_complex=False):
|
||||
BaseMPContext.__init__(ctx)
|
||||
ctx.pretty = False
|
||||
ctx.types = [ctx.mpf, ctx.mpc, ctx.constant]
|
||||
ctx.default()
|
||||
ctx._set_prec(prec)
|
||||
ctx._set_rounding(rounding)
|
||||
ctx.trap_complex = trap_complex
|
||||
StandardBaseContext.__init__(ctx)
|
||||
|
||||
@@ -359,6 +361,7 @@ class MPContext(BaseMPContext, StandardBaseContext):
|
||||
lines = ["Mpmath settings:",
|
||||
(" mp.prec = %s" % ctx.prec).ljust(30) + f"[default: {sys.float_info.mant_dig}]",
|
||||
(" mp.dps = %s" % ctx.dps).ljust(30) + f"[default: {sys.float_info.dig}]",
|
||||
(" mp.rounding = '%s'" % ctx.rounding).ljust(30) + f"[default: 'n']",
|
||||
(" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
+13
-3
@@ -130,13 +130,14 @@ class _mpf(mpnumeric):
|
||||
def __reduce__(self): return _make_mpf, (self._mpf_,)
|
||||
|
||||
def __repr__(s):
|
||||
rounding = s.context._rounding
|
||||
if s.context.pretty:
|
||||
ndigits = (s.context._repr_digits
|
||||
if s.context._pretty_repr_dps else s.context._str_digits)
|
||||
return to_str(s._mpf_, ndigits)
|
||||
return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits)
|
||||
return to_str(s._mpf_, ndigits, rounding=rounding)
|
||||
return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits, rounding=rounding)
|
||||
|
||||
def __str__(s): return to_str(s._mpf_, s.context._str_digits)
|
||||
def __str__(s): return to_str(s._mpf_, s.context._str_digits, rounding=s.context._rounding)
|
||||
def __hash__(s): return mpf_hash(s._mpf_)
|
||||
def __int__(s): return int(to_int(s._mpf_))
|
||||
def __float__(s): return to_float(s._mpf_, rnd=s.context._prec_rounding[1])
|
||||
@@ -730,6 +731,7 @@ class PythonMPContext:
|
||||
|
||||
def default(ctx):
|
||||
ctx._prec = ctx._prec_rounding[0] = sys.float_info.mant_dig
|
||||
ctx._rounding = ctx._prec_rounding[1]
|
||||
ctx._dps = sys.float_info.dig
|
||||
ctx.trap_complex = False
|
||||
|
||||
@@ -741,8 +743,16 @@ class PythonMPContext:
|
||||
ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n)
|
||||
ctx._dps = max(1, int(n))
|
||||
|
||||
def _set_rounding(ctx, r):
|
||||
try:
|
||||
ctx._prec_rounding[1] = ctx._parse_prec({'rounding': r})[1]
|
||||
ctx._rounding = ctx._prec_rounding[1]
|
||||
except KeyError:
|
||||
raise ValueError('invalid rounding mode')
|
||||
|
||||
prec = property(lambda ctx: ctx._prec, _set_prec)
|
||||
dps = property(lambda ctx: ctx._dps, _set_dps)
|
||||
rounding = property(lambda ctx: ctx._rounding, _set_rounding)
|
||||
|
||||
def _set_pretty_dps(ctx, v):
|
||||
ctx._pretty_repr_dps = True if v == 'repr' else False
|
||||
|
||||
@@ -288,11 +288,11 @@ def test_arithmetic_functions():
|
||||
assert fneg(z1) == -(+z1)
|
||||
|
||||
def test_exact_integer_arithmetic():
|
||||
# XXX: re-fix this so that all operations are tested with all rounding modes
|
||||
random.seed(0)
|
||||
for prec in [6, 10, 25, 40, 100, 250, 725]:
|
||||
for rounding in ['d', 'u', 'f', 'c', 'n']:
|
||||
mp.dps = prec
|
||||
mp.rounding = rounding
|
||||
M = 10**(prec-2)
|
||||
M2 = 10**(prec//2-2)
|
||||
for i in range(10):
|
||||
@@ -665,3 +665,13 @@ def test_round_bulk(x, n):
|
||||
return
|
||||
assert nstr(mr, n=14, base=16, strip_zeros=False,
|
||||
show_zero_exponent=True, binary_exp=True) == xr.hex()
|
||||
|
||||
|
||||
def test_rounding_prop():
|
||||
assert mp.rounding == 'n'
|
||||
assert mp.sin(1) == mpf('0x1.aed548f090ceep-1')
|
||||
mp.rounding = 'u'
|
||||
assert mp.rounding == 'u'
|
||||
assert mp.sin(1) == mpf('0x1.aed548f090cefp-1')
|
||||
with pytest.raises(ValueError):
|
||||
mp.rounding = 'x'
|
||||
|
||||
@@ -653,9 +653,8 @@ def test_root():
|
||||
r = nthroot(a, -n)
|
||||
r1 = pow(a, -mpf(1)/n)
|
||||
assert r.ae(r1)
|
||||
# XXX: this is broken right now
|
||||
# tests for nthroot rounding
|
||||
for rnd in ['nearest', 'up', 'down']:
|
||||
for rnd in ['n', 'u', 'd']:
|
||||
mp.rounding = rnd
|
||||
for n in [-5, -3, 3, 5]:
|
||||
prec = 50
|
||||
@@ -667,6 +666,7 @@ def test_root():
|
||||
mp.prec = prec
|
||||
r = nthroot(b, n)
|
||||
assert r == a
|
||||
mp.rounding = 'n'
|
||||
mp.dps = 30
|
||||
for n in range(3, 21):
|
||||
a = (random.random() + j*random.random())
|
||||
|
||||
Reference in New Issue
Block a user