Merge pull request #966 from skirpichev/misc

Misc fixes
This commit is contained in:
Sergey B Kirpichev
2025-06-04 15:33:55 +03:00
committed by GitHub
5 changed files with 49 additions and 40 deletions
+6 -4
View File
@@ -30,8 +30,8 @@ Features:
* Add signed option to to_man_exp(), see #783 (Sergey B Kirpichev)
* Add fp.hypot, see #798 (Sergey B Kirpichev)
* Support inf/nan's in ctx.almosteq(), #802 (Sergey B Kirpichev)
* Implement mpf.__format__(), see #819, #831, #850, #859, #857, #862, #881 and
#944 (Javier Garcia, Sergey B Kirpichev)
* Implement mpf.__format__(), see #819, #831, #850, #859, #857, #862, #881,
#944 and #966 (Javier Garcia, Sergey B Kirpichev)
* Support conversion from scalar ndarray's, see #821 (Sergey B Kirpichev)
* Support rounding modes in mpf.__format__, see #823, #831 and #834 (Javier
Garcia, Sergey B Kirpichev)
@@ -40,7 +40,7 @@ Features:
* Implement 'a'/'A' formating types for mpf.__format__, see #841 and #870
(Sergey B Kirpichev)
* Add mpc.__format__(), see #855 (Sergey B Kirpichev)
* Now mpf.__round__() returns mpf, see #826 (Sergey B Kirpichev)
* Now mpf.__round__() returns mpf, see #826 and #966 (Sergey B Kirpichev)
* Support 'b' (binary) format type for mpf/mpc, see #867 (Sergey B Kirpichev)
* Implement mpf.__floordiv__() and mpf.__divmod__(), see #873 (Sergey B
Kirpichev)
@@ -70,7 +70,8 @@ Compatibility:
* Drop to_pickable()/from_pickable() helpers, see #667 and #769 (Sergey B
Kirpichev)
* Direct access to _mpf_ tuples now deprecated, please use from/to_man_exp()
functions, see #783 (Sergey B Kirpichev)
functions and special constants (finf, fninf and fnan), see
#783 (Sergey B Kirpichev)
* Removed sage backend, see #732 (Sergey B Kirpichev)
* Drop MPMATH_STRICT environment variable, see #759 (Sergey B Kirpichev)
* Deprecate current (descending) order of coefficients in polyval(), etc, see
@@ -152,6 +153,7 @@ Bug fixes:
* Use mpf_log1p in acos_asin() helper (implementing Hull et al algorithm), see
#948 (Sergey B Kirpichev)
* Fix kwargs passing in the nstr() for mpc, see #964 (David Walker)
* Fix exception type for int(inf), see #966 (Sergey B Kirpichev)
Maintenance:
+10 -8
View File
@@ -130,14 +130,14 @@ class _mpf(mpnumeric):
def __reduce__(self): return _make_mpf, (self._mpf_,)
def __repr__(s):
rounding = s.context._rounding
rounding = s.context._prec_rounding[1]
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, 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, rounding=s.context._rounding)
def __str__(s): return to_str(s._mpf_, s.context._str_digits, rounding=s.context._prec_rounding[1])
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])
@@ -441,13 +441,17 @@ class _mpf(mpnumeric):
def to_fixed(self, prec):
return to_fixed(self._mpf_, prec)
def __round__(self, ndigits=0):
def __round__(self, ndigits=None):
ctx = self.context
if ctx.isfinite(self):
frac = MPQ(*self.as_integer_ratio())
res = round(frac, ndigits)
return ctx.convert(res)
return self
res = ctx.convert(res)
else:
res = self
if ndigits is None:
res = int(res)
return res
class _constant(_mpf):
@@ -731,7 +735,6 @@ 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
@@ -746,13 +749,12 @@ class PythonMPContext:
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)
rounding = property(lambda ctx: ctx._prec_rounding[1], _set_rounding)
def _set_pretty_dps(ctx, v):
ctx._pretty_repr_dps = True if v == 'repr' else False
+14 -17
View File
@@ -267,7 +267,9 @@ def to_int(s, rnd=round_fast):
input is inf/nan, an exception is raised."""
sign, man, exp, bc = s
if (not man) and exp:
raise ValueError("cannot convert inf or nan to int")
if s == fnan:
raise ValueError("cannot convert nan to int")
raise OverflowError("cannot convert infinity to int")
if exp >= 0:
if sign:
return (-man) << exp
@@ -1397,12 +1399,12 @@ _FLOAT_FORMAT_SPECIFICATION_MATCHER = re.compile(r"""
(?P<sign>[-+ ]?)
(?P<no_neg_0>z)?
(?P<alternate>\#)?
(?P<zeropad>0(?=[0-9]))?
(?P<width>0|[1-9][0-9]*)?
(?P<zeropad>0(?=0*[1-9]))?
(?P<width>[0-9]+)?
(?P<thousands_separators>[,_])?
(?:\.
(?=[,_0-9]) # lookahead for digit or separator
(?P<precision>0|[1-9][0-9]*)?
(?P<precision>[0-9]+)?
(?P<frac_separators>[,_])?
)?
(?P<rounding>[UDYZN])?
@@ -1478,22 +1480,17 @@ def read_format_spec(format_spec):
format_dict['rounding'] = _GMPY_ROUND_CHAR_DICT[rounding_char]
if match['zeropad']:
if match['fill_char']:
raise ValueError("Fill character conflicts with '0'"
f" in format specifier: '{format_spec}'")
if match['align']:
raise ValueError("Alignment conflicts with '0'"
f" in format specifier: '{format_spec}'")
format_dict['align'] = '='
format_dict['fill_char'] = '0'
if not match['align']:
format_dict['align'] = '='
if not match['fill_char']:
format_dict['fill_char'] = '0'
if format_dict['precision'] < 0 and (format_dict['type'].lower()
not in ['', 'a', 'b']):
if format_dict['precision'] < 0 and format_dict['type'].lower() not in ['', 'a', 'b']:
format_dict['precision'] = 6
else:
raise ValueError("Invalid format specifier '{}'".format(format_spec))
return format_dict
raise ValueError(f"Invalid format specifier '{format_spec}'")
return format_dict
def format_fixed(s, precision=6, rounding=round_nearest):
+10
View File
@@ -665,6 +665,16 @@ 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()
try:
xr = round(x)
except ValueError:
pytest.raises(ValueError, lambda: round(m))
except OverflowError:
pytest.raises(OverflowError, lambda: round(m))
else:
mr = round(m)
assert type(mr) is int
assert mr == xr
def test_rounding_prop():
+9 -11
View File
@@ -57,7 +57,9 @@ def fmt_str(draw, types='fFeE', for_complex=False):
skip_thousand_separators = True
# Width
res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))))
res += draw(st.sampled_from(['']*7 + list(map(str, range(1, 40)))
+ ([] if for_complex else ['0' + str(_)
for _ in range(40)])))
# grouping character (thousand_separators)
gchar = draw(st.sampled_from([''] + list(',_')))
@@ -65,7 +67,8 @@ def fmt_str(draw, types='fFeE', for_complex=False):
res += gchar
# Precision
prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))))
prec = draw(st.sampled_from(['']*7 + list(map(str, range(40)))
+ ['0' + str(_) for _ in range(40)]))
if prec:
res += '.' + prec
if vinfo >= (3, 14):
@@ -481,6 +484,8 @@ def test_mpf_fmt_cpython():
allow_infinity=True,
allow_subnormal=True))
@example(fmt='.0g', x=9.995074823339339e-05) # issue 880
@example(fmt='.016f', x=0.1) # issue 915
@example(fmt='0030f', x=0.3)
@example(fmt='0=13,f', x=1.1) # issue 917
@example(fmt='013,f', x=1.1)
@example(fmt='013,.0%', x=1.1)
@@ -628,8 +633,8 @@ def test_mpf_fmt():
# Tests for = alignment
assert f"{mp.mpf('0.24'):=+20.2f}" == '+ 0.24'
assert f"{mp.mpf('0.24'):0=+20.2e}" == '+000000000002.40e-01'
assert f"{mp.mpf('0.24'):0=+20.2g}" == '+0000000000000000.24'
assert f"{mp.mpf('0.24'):=+020.2e}" == '+000000000002.40e-01'
assert f"{mp.mpf('0.24'):=+020.2g}" == '+0000000000000000.24'
# Tests for different kinds of rounding
num = mp.mpf('-1.23456789999901234567')
@@ -826,13 +831,6 @@ def test_errors():
with pytest.raises(ValueError, match="Invalid format specifier '12.3 E '"):
f"{mp.mpf('4'):12.3 E }"
with pytest.raises(ValueError, match="Fill character conflicts"):
f"{mp.mpf('4'):q<03f}"
with pytest.raises(ValueError, match="Alignment conflicts"):
f"{mp.mpf('4'):=03f}"
with pytest.raises(ValueError, match="Fill character conflicts"):
f"{mp.mpf('4'): =03f}"
with pytest.raises(ValueError):
f"{mp.mpf(1):.f}"
with pytest.raises(ValueError):