Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
bpo-45995: add "z" format specifer to coerce negative 0 to zero#30049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a633e88636da053f4085d68a049ead32be53dcaf5f6b9ab3be243568be4fda2043d76a104e02361c64df76d61ae33fe72c9393136f88f7fc20c9cf1bf1a8913f5b3928d7a7452a24e610cbff6a8e7b51c418ab763ee6f6bFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1071,6 +1071,57 @@ def test_formatting(self): | ||
| (',e', '123456', '1.23456e+5'), | ||
| (',E', '123456', '1.23456E+5'), | ||
| # negative zero: default behavior | ||
belm0 marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| ('.1f', '-0', '-0.0'), | ||
| ('.1f', '-.0', '-0.0'), | ||
| ('.1f', '-.01', '-0.0'), | ||
| # negative zero: z option | ||
| ('z.1f', '0.', '0.0'), | ||
| ('z6.1f', '0.', ' 0.0'), | ||
| ('z6.1f', '-1.', ' -1.0'), | ||
| ('z.1f', '-0.', '0.0'), | ||
| ('z.1f', '.01', '0.0'), | ||
| ('z.1f', '-.01', '0.0'), | ||
| ('z.2f', '0.', '0.00'), | ||
| ('z.2f', '-0.', '0.00'), | ||
| ('z.2f', '.001', '0.00'), | ||
| ('z.2f', '-.001', '0.00'), | ||
| ('z.1e', '0.', '0.0e+1'), | ||
| ('z.1e', '-0.', '0.0e+1'), | ||
| ('z.1E', '0.', '0.0E+1'), | ||
| ('z.1E', '-0.', '0.0E+1'), | ||
| ('z.2e', '-0.001', '-1.00e-3'), # tests for mishandled rounding | ||
| ('z.2g', '-0.001', '-0.001'), | ||
| ('z.2%', '-0.001', '-0.10%'), | ||
| ('zf', '-0.0000', '0.0000'), # non-normalized form is preserved | ||
| ('z.1f', '-00000.000001', '0.0'), | ||
| ('z.1f', '-00000.', '0.0'), | ||
| ('z.1f', '-.0000000000', '0.0'), | ||
| ('z.2f', '-00000.000001', '0.00'), | ||
| ('z.2f', '-00000.', '0.00'), | ||
| ('z.2f', '-.0000000000', '0.00'), | ||
| ('z.1f', '.09', '0.1'), | ||
| ('z.1f', '-.09', '-0.1'), | ||
| (' z.0f', '-0.', ' 0'), | ||
| ('+z.0f', '-0.', '+0'), | ||
| ('-z.0f', '-0.', '0'), | ||
| (' z.0f', '-1.', '-1'), | ||
| ('+z.0f', '-1.', '-1'), | ||
| ('-z.0f', '-1.', '-1'), | ||
| ('z>6.1f', '-0.', 'zz-0.0'), | ||
| ('z>z6.1f', '-0.', 'zzz0.0'), | ||
| ('x>z6.1f', '-0.', 'xxx0.0'), | ||
| ('🖤>z6.1f', '-0.', '🖤🖤🖤0.0'), # multi-byte fill char | ||
| # issue 6850 | ||
| ('a=-7.0', '0.12345', 'aaaa0.1'), | ||
| @@ -1085,6 +1136,15 @@ def test_formatting(self): | ||
| # bytes format argument | ||
| self.assertRaises(TypeError, Decimal(1).__format__, b'-020') | ||
| def test_negative_zero_format_directed_rounding(self): | ||
| with self.decimal.localcontext() as ctx: | ||
| ctx.rounding = ROUND_CEILING | ||
| self.assertEqual(format(self.decimal.Decimal('-0.001'), 'z.2f'), | ||
| '0.00') | ||
| def test_negative_zero_bad_format(self): | ||
| self.assertRaises(ValueError, format, self.decimal.Decimal('1.23'), 'fz') | ||
| def test_n_format(self): | ||
| Decimal = self.decimal.Decimal | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -701,18 +701,16 @@ def test_format(self): | ||
| # conversion to string should fail | ||
| self.assertRaises(ValueError, format, 3.0, "s") | ||
| # other format specifiers shouldn't work on floats, | ||
| # in particular int specifiers | ||
| for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + | ||
| [chr(x) for x in range(ord('A'), ord('Z')+1)]): | ||
| if not format_spec in 'eEfFgGn%': | ||
| self.assertRaises(ValueError, format, 0.0, format_spec) | ||
| self.assertRaises(ValueError, format, 1.0, format_spec) | ||
| self.assertRaises(ValueError, format, -1.0, format_spec) | ||
| self.assertRaises(ValueError, format, 1e100, format_spec) | ||
| self.assertRaises(ValueError, format, -1e100, format_spec) | ||
| self.assertRaises(ValueError, format, 1e-100, format_spec) | ||
| self.assertRaises(ValueError, format, -1e-100, format_spec) | ||
| # confirm format options expected to fail on floats, such as integer | ||
| # presentation types | ||
| for format_spec in 'sbcdoxX': | ||
belm0 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| self.assertRaises(ValueError, format, 0.0, format_spec) | ||
| self.assertRaises(ValueError, format, 1.0, format_spec) | ||
| self.assertRaises(ValueError, format, -1.0, format_spec) | ||
| self.assertRaises(ValueError, format, 1e100, format_spec) | ||
| self.assertRaises(ValueError, format, -1e100, format_spec) | ||
| self.assertRaises(ValueError, format, 1e-100, format_spec) | ||
| self.assertRaises(ValueError, format, -1e-100, format_spec) | ||
| # issue 3382 | ||
| self.assertEqual(format(NAN, 'f'), 'nan') | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -546,6 +546,80 @@ def test_unicode_in_error_message(self): | ||
| with self.assertRaisesRegex(ValueError, str_err): | ||
| "{a:%ЫйЯЧ}".format(a='a') | ||
| def test_negative_zero(self): | ||
| ## default behavior | ||
| self.assertEqual(f"{-0.:.1f}", "-0.0") | ||
| self.assertEqual(f"{-.01:.1f}", "-0.0") | ||
| self.assertEqual(f"{-0:.1f}", "0.0") # integers do not distinguish -0 | ||
| ## z sign option | ||
| self.assertEqual(f"{0.:z.1f}", "0.0") | ||
| self.assertEqual(f"{0.:z6.1f}", " 0.0") | ||
| self.assertEqual(f"{-1.:z6.1f}", " -1.0") | ||
| self.assertEqual(f"{-0.:z.1f}", "0.0") | ||
| self.assertEqual(f"{.01:z.1f}", "0.0") | ||
| self.assertEqual(f"{-0:z.1f}", "0.0") # z is allowed for integer input | ||
| self.assertEqual(f"{-.01:z.1f}", "0.0") | ||
| self.assertEqual(f"{0.:z.2f}", "0.00") | ||
| self.assertEqual(f"{-0.:z.2f}", "0.00") | ||
| self.assertEqual(f"{.001:z.2f}", "0.00") | ||
| self.assertEqual(f"{-.001:z.2f}", "0.00") | ||
| self.assertEqual(f"{0.:z.1e}", "0.0e+00") | ||
| self.assertEqual(f"{-0.:z.1e}", "0.0e+00") | ||
| self.assertEqual(f"{0.:z.1E}", "0.0E+00") | ||
| self.assertEqual(f"{-0.:z.1E}", "0.0E+00") | ||
| self.assertEqual(f"{-0.001:z.2e}", "-1.00e-03") # tests for mishandled | ||
| # rounding | ||
| self.assertEqual(f"{-0.001:z.2g}", "-0.001") | ||
| self.assertEqual(f"{-0.001:z.2%}", "-0.10%") | ||
| self.assertEqual(f"{-00000.000001:z.1f}", "0.0") | ||
| self.assertEqual(f"{-00000.:z.1f}", "0.0") | ||
| self.assertEqual(f"{-.0000000000:z.1f}", "0.0") | ||
| self.assertEqual(f"{-00000.000001:z.2f}", "0.00") | ||
| self.assertEqual(f"{-00000.:z.2f}", "0.00") | ||
| self.assertEqual(f"{-.0000000000:z.2f}", "0.00") | ||
| self.assertEqual(f"{.09:z.1f}", "0.1") | ||
| self.assertEqual(f"{-.09:z.1f}", "-0.1") | ||
belm0 marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| self.assertEqual(f"{-0.: z.0f}", " 0") | ||
| self.assertEqual(f"{-0.:+z.0f}", "+0") | ||
| self.assertEqual(f"{-0.:-z.0f}", "0") | ||
| self.assertEqual(f"{-1.: z.0f}", "-1") | ||
| self.assertEqual(f"{-1.:+z.0f}", "-1") | ||
| self.assertEqual(f"{-1.:-z.0f}", "-1") | ||
| self.assertEqual(f"{0.j:z.1f}", "0.0+0.0j") | ||
| self.assertEqual(f"{-0.j:z.1f}", "0.0+0.0j") | ||
| self.assertEqual(f"{.01j:z.1f}", "0.0+0.0j") | ||
| self.assertEqual(f"{-.01j:z.1f}", "0.0+0.0j") | ||
belm0 marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| self.assertEqual(f"{-0.:z>6.1f}", "zz-0.0") # test fill, esp. 'z' fill | ||
| self.assertEqual(f"{-0.:z>z6.1f}", "zzz0.0") | ||
| self.assertEqual(f"{-0.:x>z6.1f}", "xxx0.0") | ||
| self.assertEqual(f"{-0.:🖤>z6.1f}", "🖤🖤🖤0.0") # multi-byte fill char | ||
| def test_specifier_z_error(self): | ||
| error_msg = re.compile("Invalid format specifier '.*z.*'") | ||
| with self.assertRaisesRegex(ValueError, error_msg): | ||
| f"{0:z+f}" # wrong position | ||
| with self.assertRaisesRegex(ValueError, error_msg): | ||
| f"{0:fz}" # wrong position | ||
| error_msg = re.escape("Negative zero coercion (z) not allowed") | ||
| with self.assertRaisesRegex(ValueError, error_msg): | ||
| f"{0:zd}" # can't apply to int presentation type | ||
| with self.assertRaisesRegex(ValueError, error_msg): | ||
| f"{'x':zs}" # can't apply to string | ||
| error_msg = re.escape("unsupported format character 'z'") | ||
| with self.assertRaisesRegex(ValueError, error_msg): | ||
| "%z.1f" % 0 # not allowed in old style string interpolation | ||
| if __name__ == "__main__": | ||
| unittest.main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Add a "z" option to the string formatting specification that coerces negative | ||
| zero floating-point values to positive zero after rounding to the format | ||
| precision. Contributed by John Belmonte. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.