Skip to content

Commit 837ebbf

Browse files
committed
address review: don't raise ValueError (precision specifies the *minimal* range)
1 parent 4adfb15 commit 837ebbf

File tree

3 files changed

+25
-24
lines changed

3 files changed

+25
-24
lines changed

Doc/library/string.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,10 @@ used from the field content.
464464

465465
For integer presentation types (excluding ``'c'``), the precision gives the
466466
minimal number of digits to appear, expanded with an appropriate number of
467-
leading zeros. Note that for non-decimal presentation types --- two's
468-
complements are used to represent signed integers, accepting values in range
469-
``[-m,m)``, where ``m=2**(k*precision-1)`` and ``k=1,3,4`` for ``'b'``, ``'o'``
470-
and ``'x'``/``'X'`` types, respectively. A precision of ``0`` is treated as
471-
equivalent to a precision of ``1`` here.
467+
leading zeros. Note that for non-decimal presentation types --- integer value
468+
interpreted as ``max(k*precision, number.bit_length())``-bit two's complement,
469+
where ``k=1,3,4`` for ``'b'``, ``'o'`` and ``'x'``/``'X'`` types, respectively.
470+
A precision of ``0`` is treated as equivalent to a precision of ``1`` here.
472471

473472
.. versionchanged:: next
474473
Precision specification allowed for integer presentation types.

Lib/test/test_long.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,8 @@ def test__format__(self):
708708
self.assertEqual(format(1234567890, '_x'), '4996_02d2')
709709
self.assertEqual(format(1234567890, '_X'), '4996_02D2')
710710
self.assertEqual(format(8086, '#.8x'), '0x00001f96')
711-
self.assertRaises(ValueError, format, 2048, '.3x')
712-
self.assertRaises(ValueError, format, -2049, '.3x')
711+
self.assertEqual(format(2048, '.3x'), '0800')
712+
self.assertEqual(format(-2049, '.3x'), '17ff')
713713

714714
# octal
715715
self.assertEqual(format(3, "o"), "3")
@@ -725,8 +725,8 @@ def test__format__(self):
725725
self.assertRaises(ValueError, format, 1234567890, ',o')
726726
self.assertEqual(format(1234567890, '_o'), '111_4540_1322')
727727
self.assertEqual(format(18, '#.3o'), '0o022')
728-
self.assertRaises(ValueError, format, 256, '.3o')
729-
self.assertRaises(ValueError, format, -257, '.3o')
728+
self.assertEqual(format(256, '.3o'), '0400')
729+
self.assertEqual(format(-257, '.3o'), '1377')
730730

731731
# binary
732732
self.assertEqual(format(3, "b"), "11")
@@ -744,10 +744,10 @@ def test__format__(self):
744744
self.assertEqual(format(-12, '.8b'), '11110100')
745745
self.assertEqual(format(73, '.8b'), '01001001')
746746
self.assertEqual(format(73, '#.8b'), '0b01001001')
747-
self.assertRaises(ValueError, format, 300, '.8b')
748-
self.assertRaises(ValueError, format, -200, '.8b')
749-
self.assertRaises(ValueError, format, 128, '.8b')
750-
self.assertRaises(ValueError, format, -129, '.8b')
747+
self.assertEqual(format(300, '.8b'), '100101100')
748+
self.assertEqual(format(-200, '.8b'), '100111000')
749+
self.assertEqual(format(128, '.8b'), '010000000')
750+
self.assertEqual(format(-129, '.8b'), '101111111')
751751

752752
# make sure these are errors
753753
self.assertRaises(ValueError, format, 3, "1.3c") # precision disallowed with 'c',

Python/formatter_unicode.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,20 +1081,26 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
10811081

10821082
/* Do the hard part, converting to a string in a given base */
10831083
if (format->precision != -1) {
1084+
int64_t precision = Py_MAX(1, format->precision);
1085+
10841086
/* Use two's complement for 'b', 'o' and 'x' formatting types */
10851087
if (format->type == 'b' || format->type == 'x'
10861088
|| format->type == 'o' || format->type == 'X')
10871089
{
1088-
int64_t shift = Py_MAX(1, format->precision);
1090+
int64_t shift = precision;
1091+
int incr = 1;
10891092

10901093
if (format->type == 'x' || format->type == 'X') {
10911094
shift *= 4;
10921095
}
10931096
else if (format->type == 'o') {
10941097
shift *= 3;
10951098
}
1096-
shift--; /* expected value in range(-2**shift, 2**shift) */
1099+
shift = Py_MAX(shift, _PyLong_NumBits(value));
1100+
shift--;
10971101

1102+
/* expected value in range(-2**n, 2**n), where n=shift
1103+
or n=shift+1 */
10981104
PyObject *mod = _PyLong_Lshift(PyLong_FromLong(1), shift);
10991105

11001106
if (mod == NULL) {
@@ -1106,9 +1112,9 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
11061112
goto done;
11071113
}
11081114
if (PyObject_RichCompareBool(value, mod, Py_LT)) {
1109-
goto range;
1115+
incr++;
11101116
}
1111-
Py_SETREF(mod, _PyLong_Lshift(mod, 1));
1117+
Py_SETREF(mod, _PyLong_Lshift(mod, incr));
11121118
tmp = PyNumber_Subtract(value, mod);
11131119
Py_DECREF(mod);
11141120
if (tmp == NULL) {
@@ -1118,16 +1124,12 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
11181124
}
11191125
else {
11201126
if (PyObject_RichCompareBool(value, mod, Py_GE)) {
1121-
range:
1122-
Py_DECREF(mod);
1123-
PyErr_Format(PyExc_ValueError,
1124-
"Expected integer in range(-2**%ld, 2**%ld)",
1125-
shift, shift);
1126-
goto done;
1127+
incr++;
11271128
}
11281129
Py_DECREF(mod);
11291130
tmp = _PyLong_Format(value, base);
11301131
}
1132+
precision += (incr - 1);
11311133
}
11321134
else {
11331135
tmp = _PyLong_Format(value, base);
@@ -1139,7 +1141,7 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
11391141
/* Prepend enough leading zeros (after the sign) */
11401142

11411143
int sign = PyUnicode_READ_CHAR(tmp, leading_chars_to_skip) == '-';
1142-
Py_ssize_t tmp2_len = format->precision + leading_chars_to_skip + sign;
1144+
Py_ssize_t tmp2_len = precision + leading_chars_to_skip + sign;
11431145
Py_ssize_t tmp_len = PyUnicode_GET_LENGTH(tmp);
11441146
Py_ssize_t gap = tmp2_len - tmp_len;
11451147

0 commit comments

Comments
 (0)