From dea8d0311ea7741d70841ade2a4815243d74c659 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 5 Mar 2025 05:44:18 +0300 Subject: [PATCH 1/3] gh-130860: fix width calculation, when separators in fractional part This amends f39a07be47 --- Lib/test/test_float.py | 2 ++ Python/formatter_unicode.c | 35 ++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 231b1047f72b39..d98f60dc46e527 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -768,6 +768,8 @@ def test_format(self): self.assertEqual(format(x, '<21._f'), '123456.123_456 ') self.assertEqual(format(x, '+.11_e'), '+1.234_561_234_56e+05') self.assertEqual(format(x, '+.11,e'), '+1.234,561,234,56e+05') + self.assertEqual(format(x, '019_._f'), '000_123_456.123_456') + self.assertEqual(format(x, '021_.10_f'), '123_456.123_456_000_0') self.assertRaises(ValueError, format, x, '._6f') self.assertRaises(ValueError, format, x, '.,_f') diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c index 6e3dcb3dd66373..dcfff2f5bc7d07 100644 --- a/Python/formatter_unicode.c +++ b/Python/formatter_unicode.c @@ -578,6 +578,22 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, } } + if (spec->n_frac == 0) { + spec->n_grouped_frac_digits = 0; + } + else { + Py_UCS4 grouping_maxchar; + spec->n_grouped_frac_digits = _PyUnicode_InsertThousandsGrouping( + NULL, 0, + NULL, 0, spec->n_frac, + spec->n_frac, + locale->grouping, locale->frac_thousands_sep, &grouping_maxchar, 1); + if (spec->n_grouped_frac_digits == -1) { + return -1; + } + *maxchar = Py_MAX(*maxchar, grouping_maxchar); + } + /* The number of chars used for non-digits and non-padding. */ n_non_digit_non_padding = spec->n_sign + spec->n_prefix + spec->n_decimal + + spec->n_frac + spec->n_remainder; @@ -585,7 +601,8 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, /* min_width can go negative, that's okay. format->width == -1 means we don't care. */ if (format->fill_char == '0' && format->align == '=') - spec->n_min_width = format->width - n_non_digit_non_padding; + spec->n_min_width = (format->width - n_non_digit_non_padding + + spec->n_frac - spec->n_grouped_frac_digits); else spec->n_min_width = 0; @@ -607,22 +624,6 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, *maxchar = Py_MAX(*maxchar, grouping_maxchar); } - if (spec->n_frac == 0) { - spec->n_grouped_frac_digits = 0; - } - else { - Py_UCS4 grouping_maxchar; - spec->n_grouped_frac_digits = _PyUnicode_InsertThousandsGrouping( - NULL, 0, - NULL, 0, spec->n_frac, - spec->n_frac, - locale->grouping, locale->frac_thousands_sep, &grouping_maxchar, 1); - if (spec->n_grouped_frac_digits == -1) { - return -1; - } - *maxchar = Py_MAX(*maxchar, grouping_maxchar); - } - /* Given the desired width and the total of digit and non-digit space we consume, see if we need any padding. format->width can be negative (meaning no padding), but this code still works in From 8ae08c6611a520c5600d4a7b8cda43ffd35b73b3 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 6 Mar 2025 07:45:35 +0300 Subject: [PATCH 2/3] address review: more tests --- Lib/test/test_float.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index d98f60dc46e527..9268ec839b20a9 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -770,6 +770,18 @@ def test_format(self): self.assertEqual(format(x, '+.11,e'), '+1.234,561,234,56e+05') self.assertEqual(format(x, '019_._f'), '000_123_456.123_456') self.assertEqual(format(x, '021_.10_f'), '123_456.123_456_000_0') + self.assertEqual(format(x, '>021_._f'), '000000123_456.123_456') + self.assertEqual(format(x, '>021._f'), '0000000123456.123_456') + self.assertEqual(format(x, '<021_._f'), '123_456.123_456000000') + self.assertEqual(format(x, '<021._f'), '123456.123_4560000000') + self.assertEqual(format(x, '021._f'), '0000000123456.123_456') + self.assertEqual(format(x, '21._f'), ' 123456.123_456') + self.assertEqual(format(x, ' 21._f'), ' 123456.123_456') + self.assertEqual(format(x, 'z=21._f'), 'zzzzzzz123456.123_456') + self.assertEqual(format(x, 'z>21._f'), 'zzzzzzz123456.123_456') + self.assertEqual(format(x, 'z<21._f'), '123456.123_456zzzzzzz') + self.assertEqual(format(x, '019_._e'), '000_001.234_561e+05') + self.assertEqual(format(x, '021_.10_e'), '001.234_561_234_6e+05') self.assertRaises(ValueError, format, x, '._6f') self.assertRaises(ValueError, format, x, '.,_f') From 7aab1b15af2ca2a8ef89998543246b7bc7ff5e8e Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 6 Mar 2025 14:53:07 +0300 Subject: [PATCH 3/3] Update Lib/test/test_float.py Co-authored-by: Serhiy Storchaka --- Lib/test/test_float.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 9268ec839b20a9..ee8e28bf75b5a8 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -768,20 +768,22 @@ def test_format(self): self.assertEqual(format(x, '<21._f'), '123456.123_456 ') self.assertEqual(format(x, '+.11_e'), '+1.234_561_234_56e+05') self.assertEqual(format(x, '+.11,e'), '+1.234,561,234,56e+05') - self.assertEqual(format(x, '019_._f'), '000_123_456.123_456') - self.assertEqual(format(x, '021_.10_f'), '123_456.123_456_000_0') + self.assertEqual(format(x, '021_._f'), '0_000_123_456.123_456') + self.assertEqual(format(x, '020_._f'), '0_000_123_456.123_456') + self.assertEqual(format(x, '+021_._f'), '+0_000_123_456.123_456') + self.assertEqual(format(x, '21_._f'), ' 123_456.123_456') self.assertEqual(format(x, '>021_._f'), '000000123_456.123_456') - self.assertEqual(format(x, '>021._f'), '0000000123456.123_456') self.assertEqual(format(x, '<021_._f'), '123_456.123_456000000') - self.assertEqual(format(x, '<021._f'), '123456.123_4560000000') - self.assertEqual(format(x, '021._f'), '0000000123456.123_456') - self.assertEqual(format(x, '21._f'), ' 123456.123_456') - self.assertEqual(format(x, ' 21._f'), ' 123456.123_456') - self.assertEqual(format(x, 'z=21._f'), 'zzzzzzz123456.123_456') - self.assertEqual(format(x, 'z>21._f'), 'zzzzzzz123456.123_456') - self.assertEqual(format(x, 'z<21._f'), '123456.123_456zzzzzzz') - self.assertEqual(format(x, '019_._e'), '000_001.234_561e+05') - self.assertEqual(format(x, '021_.10_e'), '001.234_561_234_6e+05') + self.assertEqual(format(x, '023_.10_f'), '0_123_456.123_456_000_0') + self.assertEqual(format(x, '022_.10_f'), '0_123_456.123_456_000_0') + self.assertEqual(format(x, '+023_.10_f'), '+0_123_456.123_456_000_0') + self.assertEqual(format(x, '023_.9_f'), '000_123_456.123_456_000') + self.assertEqual(format(x, '021_._e'), '0_000_001.234_561e+05') + self.assertEqual(format(x, '020_._e'), '0_000_001.234_561e+05') + self.assertEqual(format(x, '+021_._e'), '+0_000_001.234_561e+05') + self.assertEqual(format(x, '023_.10_e'), '0_001.234_561_234_6e+05') + self.assertEqual(format(x, '022_.10_e'), '0_001.234_561_234_6e+05') + self.assertEqual(format(x, '023_.9_e'), '000_001.234_561_235e+05') self.assertRaises(ValueError, format, x, '._6f') self.assertRaises(ValueError, format, x, '.,_f')