summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/conversion.rst8
-rw-r--r--Include/floatobject.h6
-rw-r--r--Lib/test/test_complex.py10
-rw-r--r--Lib/test/test_float.py7
-rw-r--r--Misc/NEWS9
-rw-r--r--Objects/complexobject.c12
-rw-r--r--Objects/floatobject.c9
-rw-r--r--Objects/stringlib/formatter.h12
-rw-r--r--Python/pystrtod.c46
9 files changed, 68 insertions, 51 deletions
diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst
index 403c183..318842c 100644
--- a/Doc/c-api/conversion.rst
+++ b/Doc/c-api/conversion.rst
@@ -119,10 +119,10 @@ The following functions provide locale-independent string to number conversions.
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:
diff --git a/Include/floatobject.h b/Include/floatobject.h
index 364b913..5b8d1a1 100644
--- a/Include/floatobject.h
+++ b/Include/floatobject.h
@@ -21,6 +21,12 @@ PyAPI_DATA(PyTypeObject) PyFloat_Type;
#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
diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py
index ac19353..1593f7b 100644
--- a/Lib/test/test_complex.py
+++ b/Lib/test/test_complex.py
@@ -445,6 +445,16 @@ class ComplexTest(unittest.TestCase):
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')
diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py
index fb6daaf..b617fa3 100644
--- a/Lib/test/test_float.py
+++ b/Lib/test/test_float.py
@@ -284,6 +284,13 @@ class FormatTestCase(unittest.TestCase):
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')
diff --git a/Misc/NEWS b/Misc/NEWS
index d388be1..dbd10b2 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,15 @@ What's New in Python 3.1 beta 1?
Core and Builtins
-----------------
+- Issue #5920: For float.__format__, change the behavior with the
+ empty presentation type (that is, not one of 'e', 'f', 'g', or 'n')
+ to be like 'g' but with at least one decimal point and with a
+ default precision of 12. Previously, the behavior the same but with
+ a default precision of 6. This more closely matches str(), and
+ reduces surprises when adding alignment flags to the empty
+ presentation type. This also affects the new complex.__format__ in
+ the same way.
+
- Implement PEP 383, Non-decodable Bytes in System Character Interfaces.
- Issue #5890: in subclasses of 'property' the __doc__ attribute was
diff --git a/Objects/complexobject.c b/Objects/complexobject.c
index 8fdbd37..0d13edf 100644
--- a/Objects/complexobject.c
+++ b/Objects/complexobject.c
@@ -330,7 +330,7 @@ complex_dealloc(PyObject *op)
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 @@ complex_format(PyComplexObject *v, char format_code)
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 @@ complex_format(PyComplexObject *v, char format_code)
} 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 @@ complex_format(PyComplexObject *v, char format_code)
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 @@ complex_format(PyComplexObject *v, char format_code)
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
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 1071fae..1074f3d 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -294,11 +294,12 @@ convert_to_double(PyObject **v, double *dbl)
}
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 @@ float_str_or_repr(PyFloatObject *v, char format_code)
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,
diff --git a/Objects/stringlib/formatter.h b/Objects/stringlib/formatter.h
index 1f3c535..3b22181 100644
--- a/Objects/stringlib/formatter.h
+++ b/Objects/stringlib/formatter.h
@@ -881,6 +881,7 @@ format_float_internal(PyObject *value,
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 @@ format_float_internal(PyObject *value,
}
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 @@ format_float_internal(PyObject *value,
}
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 @@ format_complex_internal(PyObject *value,
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 @@ format_complex_internal(PyObject *value,
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 @@ format_complex_internal(PyObject *value,
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"
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index a50d360..d36f931 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -746,18 +746,15 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
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 @@ static char *uc_float_strings[] = {
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 @@ format_float_short(double d, char format_code,
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 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
}
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;