Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8858240
Add __format__ function to the _mpf type.
javierelpianista Jul 19, 2024
c34c56d
Fixed test_format.py calling for files that were not there
javierelpianista Jul 19, 2024
8631449
Additional fixes for test_format.py
javierelpianista Jul 19, 2024
bc3e71a
Removed unnecesary imports from ctx_mp_python and libmp/__init__.py
javierelpianista Jul 19, 2024
98b8e98
Moved string formatting functions to libmpf.py
javierelpianista Jul 21, 2024
048dba5
Made to_str to use the new round_digits function
javierelpianista Jul 21, 2024
c3b5fce
Made function test_mpf_fmt_cpython really test with mp.mpf numbers.
javierelpianista Jul 21, 2024
5d42158
Added an assortment of random tests that compare mp.fp.mpf with mp.mp…
javierelpianista Jul 21, 2024
2840f97
Corrected tests for nan and inf when formatting with the + option
javierelpianista Jul 21, 2024
85765cf
Improved read_format_spec to include 0- padding and to detect wrong c…
javierelpianista Jul 21, 2024
118c860
Added some tests for instances where the format specification is wrong.
javierelpianista Jul 21, 2024
8c5e683
Modified test_str.py. 0.65625 should round to 0.6562, not 0.6563.
javierelpianista Jul 21, 2024
c182d09
Changed the read_format_spec function to use the regex from fractions.py
javierelpianista Jul 21, 2024
679434f
Modified code to remove parts that were never executed. Also, added a…
javierelpianista Jul 22, 2024
9757a1c
Modified test_format.py to exclude future format features.
javierelpianista Jul 22, 2024
b474e0e
Apply suggestions from code review
skirpichev Jul 22, 2024
d0c0798
Run isort
skirpichev Jul 22, 2024
50b9ea6
Slight refactoring for tests
skirpichev Jul 22, 2024
0e5b458
Minor reformatting for libmpf.py
skirpichev Jul 22, 2024
fc9d662
And small cleanup in test_errors()
skirpichev Jul 22, 2024
86e76cd
Use formatted string literals
skirpichev Jul 22, 2024
aeb6c56
+ fix fstrings for < 3.12
skirpichev Jul 22, 2024
2e095ab
Apply suggestions from code review
skirpichev Jul 22, 2024
d8e99c4
Programmed the no_neg_0 option for mpf.__format__.
javierelpianista Jul 23, 2024
bb90d12
Added format tests for mpf initialized from subnormal floats
javierelpianista Jul 23, 2024
256373a
Added tests to test_format.py that were only covered by random tests
javierelpianista Jul 23, 2024
3fdc261
Fix for wrong formatting in some subnormal floats
javierelpianista Jul 23, 2024
8107b8f
Moved the subnormal tests to test_mpf_float
javierelpianista Jul 23, 2024
76050d9
Update mpmath/tests/test_format.py
skirpichev Jul 24, 2024
5c0d27a
Update mpmath/libmp/libmpf.py
javierelpianista Jul 24, 2024
fe2d3bd
Added detection of rounding character to format_spec in mpf.__format__
javierelpianista Jul 25, 2024
6557636
Added support for different kinds of rounding to round_digits
javierelpianista Jul 25, 2024
54a2145
Modified round_digits to accept round_up and round_down
javierelpianista Jul 26, 2024
9b762da
Added support for rounding types in format_fixed
javierelpianista Jul 25, 2024
11f8f4b
Added support for rounding types in format_scientific
javierelpianista Jul 25, 2024
58b7c94
Merge branch 'master' into rounding_types
javierelpianista Jul 26, 2024
236d1c6
Removed non-PEP8 whitespace
javierelpianista Jul 26, 2024
34e3c5b
Apply suggestions from code review
javierelpianista Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions mpmath/libmp/libmpf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,28 +1102,39 @@ def to_digits_exp(s, dps, base=10):
exponent += len(digits) - fixdps - 1
return sign, digits, exponent

def round_digits(digits, dps, base):
def round_digits(digits, dps, base, rounding=round_nearest):
'''
Returns the rounded digits, and the number of places the decimal point was
shifted.

Supports three kinds of rounding: up, down, or nearest.
'''

assert len(digits) > dps
assert rounding in (round_nearest, round_up, round_down)

exponent = 0

rnd_digs = stddigits[(base//2 + base % 2):base]
if rounding == round_down:
return digits[:dps], 0
elif rounding == round_nearest:
rnd_digs = stddigits[(base//2 + base % 2):base]
else:
rnd_digs = stddigits[:base]

tie_up = False

round_up = False
# The first digit after dps is a 5.
if digits[dps] == rnd_digs[0]:
for i in range(dps+1, len(digits)):
if digits[i] != '0':
round_up = True
tie_up = True
break
if digits[dps-1] in stddigits[1:base:2]:
round_up = True
tie_up = True

if not tie_up:

if not round_up:
digits = digits[:dps] + stddigits[int(digits[dps], base) - 1]

# Rounding up kills some instances of "...99999"
Expand Down Expand Up @@ -1376,9 +1387,16 @@ def from_str(x, prec=0, rnd=round_fast, base=0):
(?P<width>0|[1-9][0-9]*)?
(?P<thousands_separators>[,_])?
(?:\.(?P<precision>0|[1-9][0-9]*))?
(?P<rounding>[UDZN])?
(?P<type>[eEfFgG])
""", re.DOTALL | re.VERBOSE).fullmatch

_GMPY_ROUND_CHAR_DICT = {
'U': round_ceiling,
'D': round_floor,
'Z': round_down,
'N': round_nearest
}

def calc_padding(nchars, width, align):
'''
Expand Down Expand Up @@ -1416,6 +1434,7 @@ def read_format_spec(format_spec):
'thousands_separators': '',
'width': -1,
'precision': 6,
'rounding': round_nearest,
'type': 'f'
}

Expand All @@ -1430,8 +1449,12 @@ def read_format_spec(format_spec):
or format_dict['thousands_separators']
format_dict['width'] = int(match['width'] or format_dict['width'])
format_dict['precision'] = int(match['precision'] or format_dict['precision'])
rounding_char = match['rounding']
format_dict['type'] = match['type'] or format_dict['type']

if rounding_char is not None:
format_dict['rounding'] = _GMPY_ROUND_CHAR_DICT[rounding_char]

if match['zeropad'] and match['fill_char']:
raise ValueError('Cannot specify both 0-padding and a fill '
'character')
Expand All @@ -1454,7 +1477,8 @@ def format_fixed(s,
sep_range=3,
base=10,
alternate=False,
no_neg_0=False):
no_neg_0=False,
rounding=round_nearest):
'''
Format a number into fixed point.
Returns the sign character, and the string that represents the number in
Expand Down Expand Up @@ -1491,7 +1515,7 @@ def format_fixed(s,
if no_neg_0:
sign = '' if sign_spec == '-' else sign_spec
else:
digits, exp_add = round_digits(digits, dps, base)
digits, exp_add = round_digits(digits, dps, base, rounding)
exponent += exp_add

# Here we prepend the corresponding 0s to the digits string, according
Expand Down Expand Up @@ -1544,7 +1568,8 @@ def format_scientific(s,
base=10,
capitalize=False,
alternate=False,
no_neg_0=False):
rounding=round_nearest):


sep = 'E' if capitalize else 'e'

Expand All @@ -1556,7 +1581,7 @@ def format_scientific(s,
if sign != '-' and sign_spec != '-':
sign = sign_spec

digits, exp_add = round_digits(digits, dps, base)
digits, exp_add = round_digits(digits, dps, base, rounding)
exponent += exp_add

if strip_zeros:
Expand Down Expand Up @@ -1594,6 +1619,13 @@ def format_mpf(num, format_spec):
strip_last_zero = False
strip_zeros = False

if format_dict['rounding'] == round_ceiling:
rounding = round_down if num[0] else round_up
elif format_dict['rounding'] == round_floor:
rounding = round_up if num[0] else round_down
else:
rounding = format_dict['rounding']

if fmt_type == 'g':
if not format_dict['alternate']:
strip_last_zero = True
Expand Down Expand Up @@ -1621,7 +1653,8 @@ def format_mpf(num, format_spec):
sep_range=3,
base=10,
alternate=format_dict['alternate'],
no_neg_0=format_dict['no_neg_0']
no_neg_0=format_dict['no_neg_0'],
rounding=rounding
)
else: # The format type is scientific
sign, digits = format_scientific(
Expand All @@ -1632,7 +1665,7 @@ def format_mpf(num, format_spec):
base=10,
capitalize=capitalize,
alternate=format_dict['alternate'],
no_neg_0=format_dict['no_neg_0']
rounding=rounding
)

nchars = len(digits) + len(sign)
Expand Down
85 changes: 85 additions & 0 deletions mpmath/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,91 @@ def test_mpf_fmt():
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')
assert f"{num:=.2Uf}" == "-1.23"
assert f"{num:=.2Df}" == "-1.24"
assert f"{num:=.2Zf}" == "-1.23"
assert f"{num:=.2Nf}" == "-1.23"

assert f"{num:=.3Uf}" == "-1.234"
assert f"{num:=.3Df}" == "-1.235"
assert f"{num:=.3Zf}" == "-1.234"
assert f"{num:=.3Nf}" == "-1.235"

assert f"{num:=.10Uf}" == "-1.2345678999"
assert f"{num:=.10Df}" == "-1.2345679000"
assert f"{num:=.10Zf}" == "-1.2345678999"
assert f"{num:=.10Nf}" == "-1.2345679000"

num = mp.mpf('1.23456789999901234567')
assert f"{num:=.2Uf}" == "1.24"
assert f"{num:=.2Df}" == "1.23"
assert f"{num:=.2Zf}" == "1.23"
assert f"{num:=.2Nf}" == "1.23"

assert f"{num:=.3Uf}" == "1.235"
assert f"{num:=.3Df}" == "1.234"
assert f"{num:=.3Zf}" == "1.234"
assert f"{num:=.3Nf}" == "1.235"

assert f"{num:=.10Uf}" == "1.2345679000"
assert f"{num:=.10Df}" == "1.2345678999"
assert f"{num:=.10Zf}" == "1.2345678999"
assert f"{num:=.10Nf}" == "1.2345679000"

num = mp.mpf('-123.456789999901234567')
assert f"{num:=.2Ue}" == "-1.23e+02"
assert f"{num:=.2De}" == "-1.24e+02"
assert f"{num:=.2Ze}" == "-1.23e+02"
assert f"{num:=.2Ne}" == "-1.23e+02"

assert f"{num:=.3Ue}" == "-1.234e+02"
assert f"{num:=.3De}" == "-1.235e+02"
assert f"{num:=.3Ze}" == "-1.234e+02"
assert f"{num:=.3Ne}" == "-1.235e+02"

assert f"{num:=.10Ue}" == "-1.2345678999e+02"
assert f"{num:=.10De}" == "-1.2345679000e+02"
assert f"{num:=.10Ze}" == "-1.2345678999e+02"
assert f"{num:=.10Ne}" == "-1.2345679000e+02"

num = mp.mpf('123456.789999901234567')
assert f"{num:=.2Ue}" == "1.24e+05"
assert f"{num:=.2De}" == "1.23e+05"
assert f"{num:=.2Ze}" == "1.23e+05"
assert f"{num:=.2Ne}" == "1.23e+05"

assert f"{num:=.3Ue}" == "1.235e+05"
assert f"{num:=.3De}" == "1.234e+05"
assert f"{num:=.3Ze}" == "1.234e+05"
assert f"{num:=.3Ne}" == "1.235e+05"

assert f"{num:=.10Ue}" == "1.2345679000e+05"
assert f"{num:=.10De}" == "1.2345678999e+05"
assert f"{num:=.10Ze}" == "1.2345678999e+05"
assert f"{num:=.10Ne}" == "1.2345679000e+05"

assert f"{mp.mpf('123.456'):.2Ug}" == "1.3e+02"
assert f"{mp.mpf('123.456'):.2Dg}" == "1.2e+02"
assert f"{mp.mpf('123.456'):.2Zg}" == "1.2e+02"
assert f"{mp.mpf('123.456'):.2Ng}" == "1.2e+02"

assert f"{mp.mpf('-123.456'):.2Ug}" == "-1.2e+02"
assert f"{mp.mpf('-123.456'):.2Dg}" == "-1.3e+02"
assert f"{mp.mpf('-123.456'):.2Zg}" == "-1.2e+02"
assert f"{mp.mpf('-123.456'):.2Ng}" == "-1.2e+02"

assert f"{mp.mpf('123.456'):.5Ug}" == "123.46"
assert f"{mp.mpf('123.456'):.5Dg}" == "123.45"
assert f"{mp.mpf('123.456'):.5Zg}" == "123.45"
assert f"{mp.mpf('123.456'):.5Ng}" == "123.46"

assert f"{mp.mpf('-123.456'):.5Ug}" == "-123.45"
assert f"{mp.mpf('-123.456'):.5Dg}" == "-123.46"
assert f"{mp.mpf('-123.456'):.5Zg}" == "-123.45"
assert f"{mp.mpf('-123.456'):.5Ng}" == "-123.46"


def test_mpf_fmt_special():
assert f'{inf:f}' == 'inf'
Expand Down