Index: Python/pystrtod.c =================================================================== --- Python/pystrtod.c (revision 72259) +++ Python/pystrtod.c (working copy) @@ -746,18 +746,15 @@ PyErr_BadInternalCall(); return NULL; } + /* The repr() precision (17 significant decimal digits) is the + minimal number that is guaranteed to have enough precision + so that if the number is read back in the exact same binary + value is recreated. This is true for IEEE floating point + by design, and also happens to work for all other modern + hardware. */ precision = 17; format_code = 'g'; break; - case 's': /* str format */ - /* Supplied precision is unused, must be 0. */ - if (precision != 0) { - PyErr_BadInternalCall(); - return NULL; - } - precision = 12; - format_code = 'g'; - break; default: PyErr_BadInternalCall(); return NULL; @@ -889,18 +886,19 @@ Arguments: d is the double to be converted - format_code is one of 'e', 'f', 'g', 'r' or 's'. 'e', 'f' and 'g' - correspond to '%e', '%f' and '%g'; 'r' and 's' correspond - to repr and str. + format_code is one of 'e', 'f', 'g', 'r'. 'e', 'f' and 'g' + correspond to '%e', '%f' and '%g'; 'r' corresponds to repr. mode is one of '0', '2' or '3', and is completely determined by - format_code: 'e', 'g' and 's' use mode 2; 'f' mode 3, 'r' mode 0. + format_code: 'e' and 'g' use mode 2; 'f' mode 3, 'r' mode 0. precision is the desired precision always_add_sign is nonzero if a '+' sign should be included for positive numbers add_dot_0_if_integer is nonzero if integers in non-exponential form - should have ".0" added. Only applies to format codes 'r', 's', and 'g'. + should have ".0" added. Only applies to format codes 'r' and 'g'. use_alt_formatting is nonzero if alternative formatting should be - used. Only applies to format codes 'e', 'f' and 'g'. + used. Only applies to format codes 'e', 'f' and 'g'. For code 'g', + at most one of use_alt_formatting and add_dot_0_if_integer should + be nonzero. type, if non-NULL, will be set to one of these constants to identify the type of the 'd' argument: Py_DTST_FINITE @@ -1041,13 +1039,6 @@ if (decpt <= -4 || decpt > 16) use_exp = 1; break; - case 's': - /* if we're forcing a digit after the point, convert to - exponential format at 1e11. If not, convert at 1e12. */ - if (decpt <= -4 || decpt > - (add_dot_0_if_integer ? precision-1 : precision)) - use_exp = 1; - break; default: PyErr_BadInternalCall(); goto exit; @@ -1220,17 +1211,6 @@ } break; - /* str format */ - case 's': - mode = 2; - /* Supplied precision is unused, must be 0. */ - if (precision != 0) { - PyErr_BadInternalCall(); - return NULL; - } - precision = 12; - break; - default: PyErr_BadInternalCall(); return NULL; Index: Include/floatobject.h =================================================================== --- Include/floatobject.h (revision 72258) +++ Include/floatobject.h (working copy) @@ -21,6 +21,12 @@ #define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) #define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type) +/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases, + the rounding noise created by various operations is suppressed, while + giving plenty of precision for practical use. */ + +#define PyFloat_STR_PRECISION 12 + #ifdef Py_NAN #define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN) #endif Index: Objects/complexobject.c =================================================================== --- Objects/complexobject.c (revision 72258) +++ Objects/complexobject.c (working copy) @@ -330,7 +330,7 @@ static PyObject * -complex_format(PyComplexObject *v, char format_code) +complex_format(PyComplexObject *v, int precision, char format_code) { PyObject *result = NULL; Py_ssize_t len; @@ -350,7 +350,7 @@ if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) { re = ""; im = PyOS_double_to_string(v->cval.imag, format_code, - 0, 0, NULL); + precision, 0, NULL); if (!im) { PyErr_NoMemory(); goto done; @@ -358,7 +358,7 @@ } else { /* Format imaginary part with sign, real part without */ pre = PyOS_double_to_string(v->cval.real, format_code, - 0, 0, NULL); + precision, 0, NULL); if (!pre) { PyErr_NoMemory(); goto done; @@ -366,7 +366,7 @@ re = pre; im = PyOS_double_to_string(v->cval.imag, format_code, - 0, Py_DTSF_SIGN, NULL); + precision, Py_DTSF_SIGN, NULL); if (!im) { PyErr_NoMemory(); goto done; @@ -395,13 +395,13 @@ static PyObject * complex_repr(PyComplexObject *v) { - return complex_format(v, 'r'); + return complex_format(v, 0, 'r'); } static PyObject * complex_str(PyComplexObject *v) { - return complex_format(v, 's'); + return complex_format(v, PyFloat_STR_PRECISION, 'g'); } static long Index: Objects/stringlib/formatter.h =================================================================== --- Objects/stringlib/formatter.h (revision 72258) +++ Objects/stringlib/formatter.h (working copy) @@ -881,6 +881,7 @@ int has_decimal; double val; Py_ssize_t precision = format->precision; + Py_ssize_t default_precision = 6; STRINGLIB_CHAR type = format->type; int add_pct = 0; STRINGLIB_CHAR *p; @@ -907,9 +908,10 @@ } if (type == '\0') { - /* Omitted type specifier. This is like 'g' but with at least - one digit after the decimal point. */ + /* Omitted type specifier. This is like 'g' but with at least one + digit after the decimal point, and different default precision.*/ type = 'g'; + default_precision = PyFloat_STR_PRECISION; flags |= Py_DTSF_ADD_DOT_0; } @@ -933,7 +935,7 @@ } if (precision < 0) - precision = 6; + precision = default_precision; #if PY_VERSION_HEX < 0x03010000 /* 3.1 no longer converts large 'f' to 'g'. */ @@ -1039,6 +1041,7 @@ int re_has_decimal; int im_has_decimal; Py_ssize_t precision = format->precision; + Py_ssize_t default_precision = 6; STRINGLIB_CHAR type = format->type; STRINGLIB_CHAR *p_re; STRINGLIB_CHAR *p_im; @@ -1100,6 +1103,7 @@ if (type == '\0') { /* Omitted type specifier. Should be like str(self). */ type = 'g'; + default_precision = PyFloat_STR_PRECISION; add_parens = 1; if (re == 0.0) skip_re = 1; @@ -1115,7 +1119,7 @@ type = 'f'; if (precision < 0) - precision = 6; + precision = default_precision; /* Cast "type", because if we're in unicode we need to pass a 8-bit char. This is safe, because we've restricted what "type" Index: Objects/floatobject.c =================================================================== --- Objects/floatobject.c (revision 72258) +++ Objects/floatobject.c (working copy) @@ -294,11 +294,12 @@ } static PyObject * -float_str_or_repr(PyFloatObject *v, char format_code) +float_str_or_repr(PyFloatObject *v, int precision, char format_code) { PyObject *result; char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v), - format_code, 0, Py_DTSF_ADD_DOT_0, + format_code, precision, + Py_DTSF_ADD_DOT_0, NULL); if (!buf) return PyErr_NoMemory(); @@ -310,13 +311,13 @@ static PyObject * float_repr(PyFloatObject *v) { - return float_str_or_repr(v, 'r'); + return float_str_or_repr(v, 0, 'r'); } static PyObject * float_str(PyFloatObject *v) { - return float_str_or_repr(v, 's'); + return float_str_or_repr(v, PyFloat_STR_PRECISION, 'g'); } /* Comparison is pretty much a nightmare. When comparing float to float, Index: Doc/c-api/conversion.rst =================================================================== --- Doc/c-api/conversion.rst (revision 72258) +++ Doc/c-api/conversion.rst (working copy) @@ -119,10 +119,10 @@ Convert a :ctype:`double` *val* to a string using supplied *format_code*, *precision*, and *flags*. - *format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``, ``'g'``, - ``'G'``, ``'s'``, or ``'r'``. For ``'s'`` and ``'r'``, the supplied - *precision* must be 0 and is ignored. These specify the standard - :func:`str` and :func:`repr` formats, respectively. + *format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``, + ``'g'``, ``'G'`` or ``'r'``. For ``'r'``, the supplied *precision* + must be 0 and is ignored. The ``'r'`` format code specifies the + standard :func:`repr` format. *flags* can be zero or more of the values *Py_DTSF_SIGN*, *Py_DTSF_ADD_DOT_0*, or *Py_DTSF_ALT*, or-ed together: Index: Lib/test/test_complex.py =================================================================== --- Lib/test/test_complex.py (revision 72258) +++ Lib/test/test_complex.py (working copy) @@ -445,6 +445,16 @@ self.assertEqual(format(3+0j, ''), str(3+0j)) self.assertEqual(format(3.2+0j, ''), str(3.2+0j)) + # empty presentation type should still be analogous to str, + # even when format string is nonempty (issue #5920). + self.assertEqual(format(3.2+0j, '-'), str(3.2+0j)) + self.assertEqual(format(3.2+0j, '<'), str(3.2+0j)) + z = 4/7. - 100j/7. + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '10'), str(z)) + self.assertEqual(format(1+3j, 'g'), '1+3j') self.assertEqual(format(3j, 'g'), '0+3j') self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j') Index: Lib/test/test_float.py =================================================================== --- Lib/test/test_float.py (revision 72258) +++ Lib/test/test_float.py (working copy) @@ -284,6 +284,13 @@ self.assertEqual(format(0.01, ''), '0.01') self.assertEqual(format(0.01, 'g'), '0.01') + # empty presentation type should format in the same way as str + # (issue 5920) + x = 100/7. + self.assertEqual(format(x, ''), str(x)) + self.assertEqual(format(x, '-'), str(x)) + self.assertEqual(format(x, '>'), str(x)) + self.assertEqual(format(x, '2'), str(x)) self.assertEqual(format(1.0, 'f'), '1.000000')