Skip to content

Commit 3e4c5e8

Browse files
committed
Use Py_DTSF_ALT to control the prefix
1 parent 6d721cd commit 3e4c5e8

File tree

9 files changed

+59
-42
lines changed

9 files changed

+59
-42
lines changed

Doc/library/stdtypes.rst

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,14 +2478,12 @@ Notes:
24782478
inserted before the first digit.
24792479

24802480
For floats, represent the number by a hexadecimal string in the style
2481-
``[±]0xh.[hhh]p±d``, where there is one hexadecimal digit before the
2481+
``[±][0x]h[.hhh]p±d``, where there is one hexadecimal digit before the
24822482
decimal-point character and the number of hexadecimal digits after it is
24832483
equal to the precision; if the precision is missing, then the precision is
2484-
sufficient for an exact representation of the value. If the precision is
2485-
zero and the alternate form is not specified, no decimal-point character
2486-
appears. The exponent ``d`` is written in decimal, it always contains at
2487-
least one digit, and it gives the power of 2 by which to multiply the
2488-
coefficient.
2484+
sufficient for an exact representation of the value. The exponent ``d`` is
2485+
written in decimal, it always contains at least one digit, and it gives the
2486+
power of 2 by which to multiply the coefficient.
24892487

24902488
(3)
24912489
The alternate form causes the result to always contain a decimal point, even if

Doc/library/string.rst

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -594,19 +594,19 @@ Additionally, for :class:`float` available following representation types:
594594
| Type | Meaning |
595595
+=========+==========================================================+
596596
| ``'x'`` | Represent the number by a hexadecimal string in the |
597-
| | style ``[±]0xh.hhhhp±d``, where there is one hexadecimal |
598-
| | digit before the decimal-point character and the number |
599-
| | of hexadecimal digits after it is equal to the |
600-
| | precision; if the precision is missing, then the |
597+
| | style ``[±][0x]h[.hhhh]p±d``, where there is one |
598+
| | hexadecimal digit before the decimal-point character and |
599+
| | the number of hexadecimal digits after it is equal to |
600+
| | the precision; if the precision is missing, then the |
601601
| | precision is sufficient for an exact representation of |
602-
| | the value. If the precision is zero and the ``'#'`` |
603-
| | option is not specified, no decimal-point character |
604-
| | appears. The exponent ``d`` is written in decimal, it |
602+
| | the value. The exponent ``d`` is written in decimal, it |
605603
| | always contains at least one digit, and it gives the |
606-
| | power of 2 by which to multiply the coefficient. |
604+
| | power of 2 by which to multiply the coefficient. If the |
605+
| | ``'#'`` option is specified, a leading ``'0x'`` will be |
606+
| | inserted before an integer part. |
607607
+---------+----------------------------------------------------------+
608-
| ``'X'`` | Same as ``'x'``, but uses ``0X`` prefix and ``'P'`` as |
609-
| | the exponent separator. |
608+
| ``'X'`` | Same as ``'x'``, but uses uppercase digits, ``0X`` |
609+
| | prefix and ``'P'`` as the exponent separator. |
610610
+---------+----------------------------------------------------------+
611611

612612
.. versionchanged:: 3.13

Lib/test/test_float.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -702,17 +702,21 @@ def test_format(self):
702702

703703
# hexadecimal format
704704
x = float.fromhex('0x0.0030p+0')
705-
self.assertEqual(format(x, 'x'), '0x1.8p-11')
705+
self.assertEqual(format(x, 'x'), '1.8p-11')
706706
x = float.fromhex('0x0.0040p+0')
707-
self.assertEqual(format(x, 'x'), '0x1p-10')
708-
self.assertEqual(format(x, '>10x'), ' 0x1p-10')
709-
self.assertEqual(format(x, '>#10x'), ' 0x1.p-10')
710-
self.assertEqual(format(x, '<10x'), '0x1p-10 ')
711-
self.assertEqual(format(x, '<#10x'), '0x1.p-10 ')
707+
self.assertEqual(format(x, 'x'), '1p-10')
708+
self.assertEqual(format(x, '>10x'), ' 1p-10')
709+
self.assertEqual(format(x, '>#10x'), ' 0x1p-10')
710+
self.assertEqual(format(x, '>010x'), '000001p-10')
711+
self.assertEqual(format(x, '>#010x'), '0000x1p-10')
712+
self.assertEqual(format(x, '#010x'), '0x0001p-10')
713+
self.assertEqual(format(x, '<10x'), '1p-10 ')
714+
self.assertEqual(format(x, '<#10x'), '0x1p-10 ')
712715
x = float.fromhex('0x1.fe12p0')
713-
self.assertEqual(format(x, 'x'), '0x1.fe12p+0')
714-
self.assertEqual(format(x, '.3x'), '0x1.fe1p+0')
715-
self.assertEqual(format(x, '.1x'), '0x1p+1')
716+
self.assertEqual(format(x, 'x'), '1.fe12p+0')
717+
self.assertEqual(format(x, '.3x'), '1.fe1p+0')
718+
self.assertEqual(format(x, '.1x'), '1p+1')
719+
self.assertEqual(format(x, '#.1x'), '0x1p+1')
716720

717721
# conversion to string should fail
718722
self.assertRaises(ValueError, format, 3.0, "s")

Lib/test/test_format.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,15 @@ def test_common_format(self):
270270
testcommon('%#g', 1.1, '1.10000')
271271

272272
# hexadecimal floats
273-
testcommon("%x", 3.14, '0x1.91eb851eb851fp+1')
274-
testcommon("%X", 3.14, '0X1.91EB851EB851FP+1')
275-
testcommon("%+.3x", 3.14, '+0x1.91fp+1')
276-
testcommon("%x", -0.5, '-0x1p-1')
277-
testcommon("%#x", -0.5, '-0x1.p-1')
273+
testcommon("%x", 3.14, '1.91eb851eb851fp+1')
274+
testcommon("%#x", 3.14, '0x1.91eb851eb851fp+1')
275+
testcommon("%X", 3.14, '1.91EB851EB851FP+1')
276+
testcommon("%+.3x", 3.14, '+1.91fp+1')
277+
testcommon("%x", -0.5, '-1p-1')
278+
testcommon("%#x", -0.5, '-0x1p-1')
279+
x = float.fromhex('0x0.003p+0')
280+
testcommon("%040x", x, '0000000000000000000000000000000001.8p-11')
281+
testcommon("%#040x", x, '0x00000000000000000000000000000001.8p-11')
278282

279283
if verbose:
280284
print('Testing exceptions')

Lib/test/test_peepholer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ def format(fmt, *values):
567567
self.assertEqual(format('x = %s%% %%%%', 1234), 'x = 1234% %%')
568568
self.assertEqual(format('x = %s!', '%% %s'), 'x = %% %s!')
569569
self.assertEqual(format('x = %s, y = %d', 12, 34), 'x = 12, y = 34')
570-
self.assertEqual(format('x = %x', 1234.56), 'x = 0x1.34a3d70a3d70ap+10')
570+
self.assertEqual(format('x = %x', 1234.56), 'x = 1.34a3d70a3d70ap+10')
571571

572572
def test_format_errors(self):
573573
with self.assertRaisesRegex(TypeError,

Lib/test/test_str.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,8 +1564,8 @@ def __int__(self):
15641564
self.assertEqual('%X' % letter_m, '6D')
15651565
self.assertEqual('%o' % letter_m, '155')
15661566
self.assertEqual('%c' % letter_m, 'm')
1567-
self.assertEqual('%x' % 3.14, '0x1.91eb851eb851fp+1')
1568-
self.assertEqual('%X' % 2.11, '0X1.0E147AE147AE1P+1')
1567+
self.assertEqual('%x' % 3.14, '1.91eb851eb851fp+1')
1568+
self.assertEqual('%X' % 2.11, '1.0E147AE147AE1P+1')
15691569
self.assertRaisesRegex(TypeError, '%o format: an integer is required, not float', operator.mod, '%o', 1.79)
15701570
self.assertRaisesRegex(TypeError, '%x format: an integer or float is required, not PseudoFloat', operator.mod, '%x', pi)
15711571
self.assertRaisesRegex(TypeError, '%x format: an integer or float is required, not complex', operator.mod, '%x', 3j)

Objects/floatobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ float_hex_impl(PyObject *self)
12191219
{
12201220
PyObject *result = NULL;
12211221
double x = PyFloat_AS_DOUBLE(self);
1222-
char *buf = PyOS_double_to_string(x, 'x', -1, 0, NULL);
1222+
char *buf = PyOS_double_to_string(x, 'x', -1, Py_DTSF_ALT, NULL);
12231223

12241224
if (buf) {
12251225
result = PyUnicode_FromString(buf);

Python/formatter_unicode.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ format_float_internal(PyObject *value,
10471047
Py_ssize_t n_digits;
10481048
Py_ssize_t n_remainder;
10491049
Py_ssize_t n_total;
1050+
Py_ssize_t n_prefix = 0;
10501051
int has_decimal;
10511052
double val;
10521053
int precision, default_precision = 6;
@@ -1059,7 +1060,7 @@ format_float_internal(PyObject *value,
10591060
Py_UCS4 maxchar = 127;
10601061
Py_UCS4 sign_char = '\0';
10611062
int float_type; /* Used to see if we have a nan, inf, or regular float. */
1062-
PyObject *unicode_tmp = NULL;
1063+
PyObject *unicode_tmp = NULL, *prefix = NULL;
10631064

10641065
/* Locale settings, either from the actual locale or
10651066
from a hard-code pseudo-locale */
@@ -1148,6 +1149,14 @@ format_float_internal(PyObject *value,
11481149
++index;
11491150
--n_digits;
11501151
}
1152+
if (PyUnicode_READ_CHAR(unicode_tmp, index) == '0'
1153+
&& (PyUnicode_READ_CHAR(unicode_tmp, index + 1) == 'x'
1154+
|| PyUnicode_READ_CHAR(unicode_tmp, index + 1) == 'X')) {
1155+
n_prefix = 2;
1156+
index += 2;
1157+
n_digits -= 2;
1158+
prefix = unicode_tmp;
1159+
}
11511160

11521161
/* Determine if we have any "remainder" (after the digits, might include
11531162
decimal or exponent or both (or neither)) */
@@ -1160,7 +1169,7 @@ format_float_internal(PyObject *value,
11601169
goto done;
11611170

11621171
/* Calculate how much memory we'll need. */
1163-
n_total = calc_number_widths(&spec, 0, sign_char, index,
1172+
n_total = calc_number_widths(&spec, n_prefix, sign_char, index,
11641173
index + n_digits, n_remainder, has_decimal,
11651174
&locale, format, &maxchar);
11661175
if (n_total == -1) {
@@ -1174,7 +1183,7 @@ format_float_internal(PyObject *value,
11741183
/* Populate the memory. */
11751184
result = fill_number(writer, &spec,
11761185
unicode_tmp, index,
1177-
NULL, 0, format->fill_char,
1186+
prefix, n_prefix ? index - 2 : 0, format->fill_char,
11781187
&locale, 0);
11791188

11801189
done:

Python/pystrtod.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -980,10 +980,12 @@ _Py_dg_dtoa_hex(double x, int precision, int always_add_sign,
980980
s[si] = '+';
981981
si++;
982982
}
983-
s[si] = '0';
984-
si++;
985-
s[si] = upper ? 'X' : 'x';
986-
si++;
983+
if (use_alt_formatting) {
984+
s[si] = '0';
985+
si++;
986+
s[si] = upper ? 'X' : 'x';
987+
si++;
988+
}
987989

988990
/* mantissa */
989991
const char *hexmap = upper ? Py_hexdigits_upper : Py_hexdigits;
@@ -1004,7 +1006,7 @@ _Py_dg_dtoa_hex(double x, int precision, int always_add_sign,
10041006
while (s[si] == '0') {
10051007
si--;
10061008
}
1007-
if (s[si] != '.' || use_alt_formatting) {
1009+
if (s[si] != '.') {
10081010
si++;
10091011
}
10101012

0 commit comments

Comments
 (0)