Skip to content

Commit 6337622

Browse files
committed
Issue #5920: Changed format.__float__ and complex.__float__ to use a precision of 12 when using the empty presentation type. This more closely matches str()'s behavior and reduces surprises when adding alignment flags to an empty format string. Patch by Mark Dickinson.
1 parent 86a05ec commit 6337622

File tree

9 files changed

+68
-51
lines changed

9 files changed

+68
-51
lines changed

Doc/c-api/conversion.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ The following functions provide locale-independent string to number conversions.
119119
Convert a :ctype:`double` *val* to a string using supplied
120120
*format_code*, *precision*, and *flags*.
121121

122-
*format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``, ``'g'``,
123-
``'G'``, ``'s'``, or ``'r'``. For ``'s'`` and ``'r'``, the supplied
124-
*precision* must be 0 and is ignored. These specify the standard
125-
:func:`str` and :func:`repr` formats, respectively.
122+
*format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``,
123+
``'g'``, ``'G'`` or ``'r'``. For ``'r'``, the supplied *precision*
124+
must be 0 and is ignored. The ``'r'`` format code specifies the
125+
standard :func:`repr` format.
126126

127127
*flags* can be zero or more of the values *Py_DTSF_SIGN*,
128128
*Py_DTSF_ADD_DOT_0*, or *Py_DTSF_ALT*, or-ed together:

Include/floatobject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ PyAPI_DATA(PyTypeObject) PyFloat_Type;
2121
#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type)
2222
#define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type)
2323

24+
/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases,
25+
the rounding noise created by various operations is suppressed, while
26+
giving plenty of precision for practical use. */
27+
28+
#define PyFloat_STR_PRECISION 12
29+
2430
#ifdef Py_NAN
2531
#define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN)
2632
#endif

Lib/test/test_complex.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,16 @@ def test_format(self):
445445
self.assertEqual(format(3+0j, ''), str(3+0j))
446446
self.assertEqual(format(3.2+0j, ''), str(3.2+0j))
447447

448+
# empty presentation type should still be analogous to str,
449+
# even when format string is nonempty (issue #5920).
450+
self.assertEqual(format(3.2+0j, '-'), str(3.2+0j))
451+
self.assertEqual(format(3.2+0j, '<'), str(3.2+0j))
452+
z = 4/7. - 100j/7.
453+
self.assertEqual(format(z, ''), str(z))
454+
self.assertEqual(format(z, '-'), str(z))
455+
self.assertEqual(format(z, '<'), str(z))
456+
self.assertEqual(format(z, '10'), str(z))
457+
448458
self.assertEqual(format(1+3j, 'g'), '1+3j')
449459
self.assertEqual(format(3j, 'g'), '0+3j')
450460
self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j')

Lib/test/test_float.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,13 @@ def test_format(self):
284284
self.assertEqual(format(0.01, ''), '0.01')
285285
self.assertEqual(format(0.01, 'g'), '0.01')
286286

287+
# empty presentation type should format in the same way as str
288+
# (issue 5920)
289+
x = 100/7.
290+
self.assertEqual(format(x, ''), str(x))
291+
self.assertEqual(format(x, '-'), str(x))
292+
self.assertEqual(format(x, '>'), str(x))
293+
self.assertEqual(format(x, '2'), str(x))
287294

288295
self.assertEqual(format(1.0, 'f'), '1.000000')
289296

Misc/NEWS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ What's New in Python 3.1 beta 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5920: For float.__format__, change the behavior with the
16+
empty presentation type (that is, not one of 'e', 'f', 'g', or 'n')
17+
to be like 'g' but with at least one decimal point and with a
18+
default precision of 12. Previously, the behavior the same but with
19+
a default precision of 6. This more closely matches str(), and
20+
reduces surprises when adding alignment flags to the empty
21+
presentation type. This also affects the new complex.__format__ in
22+
the same way.
23+
1524
- Implement PEP 383, Non-decodable Bytes in System Character Interfaces.
1625

1726
- Issue #5890: in subclasses of 'property' the __doc__ attribute was

Objects/complexobject.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ complex_dealloc(PyObject *op)
330330

331331

332332
static PyObject *
333-
complex_format(PyComplexObject *v, char format_code)
333+
complex_format(PyComplexObject *v, int precision, char format_code)
334334
{
335335
PyObject *result = NULL;
336336
Py_ssize_t len;
@@ -350,23 +350,23 @@ complex_format(PyComplexObject *v, char format_code)
350350
if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
351351
re = "";
352352
im = PyOS_double_to_string(v->cval.imag, format_code,
353-
0, 0, NULL);
353+
precision, 0, NULL);
354354
if (!im) {
355355
PyErr_NoMemory();
356356
goto done;
357357
}
358358
} else {
359359
/* Format imaginary part with sign, real part without */
360360
pre = PyOS_double_to_string(v->cval.real, format_code,
361-
0, 0, NULL);
361+
precision, 0, NULL);
362362
if (!pre) {
363363
PyErr_NoMemory();
364364
goto done;
365365
}
366366
re = pre;
367367

368368
im = PyOS_double_to_string(v->cval.imag, format_code,
369-
0, Py_DTSF_SIGN, NULL);
369+
precision, Py_DTSF_SIGN, NULL);
370370
if (!im) {
371371
PyErr_NoMemory();
372372
goto done;
@@ -395,13 +395,13 @@ complex_format(PyComplexObject *v, char format_code)
395395
static PyObject *
396396
complex_repr(PyComplexObject *v)
397397
{
398-
return complex_format(v, 'r');
398+
return complex_format(v, 0, 'r');
399399
}
400400

401401
static PyObject *
402402
complex_str(PyComplexObject *v)
403403
{
404-
return complex_format(v, 's');
404+
return complex_format(v, PyFloat_STR_PRECISION, 'g');
405405
}
406406

407407
static long

Objects/floatobject.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,12 @@ convert_to_double(PyObject **v, double *dbl)
294294
}
295295

296296
static PyObject *
297-
float_str_or_repr(PyFloatObject *v, char format_code)
297+
float_str_or_repr(PyFloatObject *v, int precision, char format_code)
298298
{
299299
PyObject *result;
300300
char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
301-
format_code, 0, Py_DTSF_ADD_DOT_0,
301+
format_code, precision,
302+
Py_DTSF_ADD_DOT_0,
302303
NULL);
303304
if (!buf)
304305
return PyErr_NoMemory();
@@ -310,13 +311,13 @@ float_str_or_repr(PyFloatObject *v, char format_code)
310311
static PyObject *
311312
float_repr(PyFloatObject *v)
312313
{
313-
return float_str_or_repr(v, 'r');
314+
return float_str_or_repr(v, 0, 'r');
314315
}
315316

316317
static PyObject *
317318
float_str(PyFloatObject *v)
318319
{
319-
return float_str_or_repr(v, 's');
320+
return float_str_or_repr(v, PyFloat_STR_PRECISION, 'g');
320321
}
321322

322323
/* Comparison is pretty much a nightmare. When comparing float to float,

Objects/stringlib/formatter.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,7 @@ format_float_internal(PyObject *value,
881881
int has_decimal;
882882
double val;
883883
Py_ssize_t precision = format->precision;
884+
Py_ssize_t default_precision = 6;
884885
STRINGLIB_CHAR type = format->type;
885886
int add_pct = 0;
886887
STRINGLIB_CHAR *p;
@@ -907,9 +908,10 @@ format_float_internal(PyObject *value,
907908
}
908909

909910
if (type == '\0') {
910-
/* Omitted type specifier. This is like 'g' but with at least
911-
one digit after the decimal point. */
911+
/* Omitted type specifier. This is like 'g' but with at least one
912+
digit after the decimal point, and different default precision.*/
912913
type = 'g';
914+
default_precision = PyFloat_STR_PRECISION;
913915
flags |= Py_DTSF_ADD_DOT_0;
914916
}
915917

@@ -933,7 +935,7 @@ format_float_internal(PyObject *value,
933935
}
934936

935937
if (precision < 0)
936-
precision = 6;
938+
precision = default_precision;
937939

938940
#if PY_VERSION_HEX < 0x03010000
939941
/* 3.1 no longer converts large 'f' to 'g'. */
@@ -1039,6 +1041,7 @@ format_complex_internal(PyObject *value,
10391041
int re_has_decimal;
10401042
int im_has_decimal;
10411043
Py_ssize_t precision = format->precision;
1044+
Py_ssize_t default_precision = 6;
10421045
STRINGLIB_CHAR type = format->type;
10431046
STRINGLIB_CHAR *p_re;
10441047
STRINGLIB_CHAR *p_im;
@@ -1100,6 +1103,7 @@ format_complex_internal(PyObject *value,
11001103
if (type == '\0') {
11011104
/* Omitted type specifier. Should be like str(self). */
11021105
type = 'g';
1106+
default_precision = PyFloat_STR_PRECISION;
11031107
add_parens = 1;
11041108
if (re == 0.0)
11051109
skip_re = 1;
@@ -1115,7 +1119,7 @@ format_complex_internal(PyObject *value,
11151119
type = 'f';
11161120

11171121
if (precision < 0)
1118-
precision = 6;
1122+
precision = default_precision;
11191123

11201124
/* Cast "type", because if we're in unicode we need to pass a
11211125
8-bit char. This is safe, because we've restricted what "type"

Python/pystrtod.c

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -746,18 +746,15 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
746746
PyErr_BadInternalCall();
747747
return NULL;
748748
}
749+
/* The repr() precision (17 significant decimal digits) is the
750+
minimal number that is guaranteed to have enough precision
751+
so that if the number is read back in the exact same binary
752+
value is recreated. This is true for IEEE floating point
753+
by design, and also happens to work for all other modern
754+
hardware. */
749755
precision = 17;
750756
format_code = 'g';
751757
break;
752-
case 's': /* str format */
753-
/* Supplied precision is unused, must be 0. */
754-
if (precision != 0) {
755-
PyErr_BadInternalCall();
756-
return NULL;
757-
}
758-
precision = 12;
759-
format_code = 'g';
760-
break;
761758
default:
762759
PyErr_BadInternalCall();
763760
return NULL;
@@ -889,18 +886,19 @@ static char *uc_float_strings[] = {
889886
890887
Arguments:
891888
d is the double to be converted
892-
format_code is one of 'e', 'f', 'g', 'r' or 's'. 'e', 'f' and 'g'
893-
correspond to '%e', '%f' and '%g'; 'r' and 's' correspond
894-
to repr and str.
889+
format_code is one of 'e', 'f', 'g', 'r'. 'e', 'f' and 'g'
890+
correspond to '%e', '%f' and '%g'; 'r' corresponds to repr.
895891
mode is one of '0', '2' or '3', and is completely determined by
896-
format_code: 'e', 'g' and 's' use mode 2; 'f' mode 3, 'r' mode 0.
892+
format_code: 'e' and 'g' use mode 2; 'f' mode 3, 'r' mode 0.
897893
precision is the desired precision
898894
always_add_sign is nonzero if a '+' sign should be included for positive
899895
numbers
900896
add_dot_0_if_integer is nonzero if integers in non-exponential form
901-
should have ".0" added. Only applies to format codes 'r', 's', and 'g'.
897+
should have ".0" added. Only applies to format codes 'r' and 'g'.
902898
use_alt_formatting is nonzero if alternative formatting should be
903-
used. Only applies to format codes 'e', 'f' and 'g'.
899+
used. Only applies to format codes 'e', 'f' and 'g'. For code 'g',
900+
at most one of use_alt_formatting and add_dot_0_if_integer should
901+
be nonzero.
904902
type, if non-NULL, will be set to one of these constants to identify
905903
the type of the 'd' argument:
906904
Py_DTST_FINITE
@@ -1041,13 +1039,6 @@ format_float_short(double d, char format_code,
10411039
if (decpt <= -4 || decpt > 16)
10421040
use_exp = 1;
10431041
break;
1044-
case 's':
1045-
/* if we're forcing a digit after the point, convert to
1046-
exponential format at 1e11. If not, convert at 1e12. */
1047-
if (decpt <= -4 || decpt >
1048-
(add_dot_0_if_integer ? precision-1 : precision))
1049-
use_exp = 1;
1050-
break;
10511042
default:
10521043
PyErr_BadInternalCall();
10531044
goto exit;
@@ -1220,17 +1211,6 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
12201211
}
12211212
break;
12221213

1223-
/* str format */
1224-
case 's':
1225-
mode = 2;
1226-
/* Supplied precision is unused, must be 0. */
1227-
if (precision != 0) {
1228-
PyErr_BadInternalCall();
1229-
return NULL;
1230-
}
1231-
precision = 12;
1232-
break;
1233-
12341214
default:
12351215
PyErr_BadInternalCall();
12361216
return NULL;

0 commit comments

Comments
 (0)