diff options
-rw-r--r-- | Doc/c-api/conversion.rst | 8 | ||||
-rw-r--r-- | Include/floatobject.h | 6 | ||||
-rw-r--r-- | Lib/test/test_complex.py | 10 | ||||
-rw-r--r-- | Lib/test/test_float.py | 47 | ||||
-rw-r--r-- | Misc/NEWS | 9 | ||||
-rw-r--r-- | Objects/complexobject.c | 17 | ||||
-rw-r--r-- | Objects/floatobject.c | 15 | ||||
-rw-r--r-- | Objects/stringlib/formatter.h | 12 | ||||
-rw-r--r-- | Python/pystrtod.c | 13 |
9 files changed, 110 insertions, 27 deletions
diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index 4d1f42f..fc42508 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -86,10 +86,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 60ede40..6c11036 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 557e952..56e9083 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -467,6 +467,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 971c14f..1c6c412 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -257,6 +257,53 @@ class IEEEFormatTestCase(unittest.TestCase): self.assertEquals(math.atan2(float('-1e-1000'), -1), math.atan2(-0.0, -1)) + def test_format(self): + # these should be rewritten to use both format(x, spec) and + # x.__format__(spec) + + self.assertEqual(format(0.0, 'f'), '0.000000') + + # the default is 'g', except for empty format spec + self.assertEqual(format(0.0, ''), '0.0') + 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') + + self.assertEqual(format(-1.0, 'f'), '-1.000000') + + self.assertEqual(format( 1.0, ' f'), ' 1.000000') + self.assertEqual(format(-1.0, ' f'), '-1.000000') + self.assertEqual(format( 1.0, '+f'), '+1.000000') + self.assertEqual(format(-1.0, '+f'), '-1.000000') + + # % formatting + self.assertEqual(format(-1.0, '%'), '-100.000000%') + + # conversion to string should fail + self.assertRaises(ValueError, format, 3.0, "s") + + # other format specifiers shouldn't work on floats, + # in particular int specifiers + for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + + [chr(x) for x in range(ord('A'), ord('Z')+1)]): + if not format_spec in 'eEfFgGn%': + self.assertRaises(ValueError, format, 0.0, format_spec) + self.assertRaises(ValueError, format, 1.0, format_spec) + self.assertRaises(ValueError, format, -1.0, format_spec) + self.assertRaises(ValueError, format, 1e100, format_spec) + self.assertRaises(ValueError, format, -1e100, format_spec) + self.assertRaises(ValueError, format, 1e-100, format_spec) + self.assertRaises(ValueError, format, -1e-100, format_spec) + @unittest.skipUnless(float.__getformat__("double").startswith("IEEE"), "test requires IEEE 754 doubles") def test_format_testfile(self): @@ -12,6 +12,15 @@ What's New in Python 2.7 alpha 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. + - Issue #5890: in subclasses of 'property' the __doc__ attribute was shadowed by classtype's, even if it was None. property now inserts the __doc__ into the subclass instance __dict__. diff --git a/Objects/complexobject.c b/Objects/complexobject.c index f0f2541..8bba241 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -354,7 +354,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; @@ -374,7 +374,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; @@ -382,7 +382,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; @@ -390,7 +390,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; @@ -421,7 +421,10 @@ complex_print(PyComplexObject *v, FILE *fp, int flags) { PyObject *formatv; char *buf; - formatv = complex_format(v, (flags & Py_PRINT_RAW) ? 's' : 'r'); + if (flags & Py_PRINT_RAW) + formatv = complex_format(v, PyFloat_STR_PRECISION, 'g'); + else + formatv = complex_format(v, 0, 'r'); if (formatv == NULL) return -1; buf = PyString_AS_STRING(formatv); @@ -435,13 +438,13 @@ complex_print(PyComplexObject *v, FILE *fp, int flags) 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 9a0dbb3..6b9a310 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -352,7 +352,7 @@ convert_to_double(PyObject **v, double *dbl) void PyFloat_AsString(char *buf, PyFloatObject *v) { - _PyOS_double_to_string(buf, 100, v->ob_fval, 's', 0, + _PyOS_double_to_string(buf, 100, v->ob_fval, 'g', PyFloat_STR_PRECISION, Py_DTSF_ADD_DOT_0, NULL); } @@ -368,9 +368,13 @@ static int float_print(PyFloatObject *v, FILE *fp, int flags) { char buf[100]; - _PyOS_double_to_string(buf, sizeof(buf), v->ob_fval, - (flags & Py_PRINT_RAW) ? 's' : 'r', - 0, Py_DTSF_ADD_DOT_0, NULL); + if (flags & Py_PRINT_RAW) + _PyOS_double_to_string(buf, sizeof(buf), v->ob_fval, + 'g', PyFloat_STR_PRECISION, + Py_DTSF_ADD_DOT_0, NULL); + else + _PyOS_double_to_string(buf, sizeof(buf), v->ob_fval, + 'r', 0, Py_DTSF_ADD_DOT_0, NULL); Py_BEGIN_ALLOW_THREADS fputs(buf, fp); Py_END_ALLOW_THREADS @@ -390,7 +394,8 @@ static PyObject * float_str(PyFloatObject *v) { char buf[100]; - _PyOS_double_to_string(buf, sizeof(buf), v->ob_fval, 's', 0, + _PyOS_double_to_string(buf, sizeof(buf), v->ob_fval, 'g', + PyFloat_STR_PRECISION, Py_DTSF_ADD_DOT_0, NULL); return PyString_FromString(buf); } 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 2be3834..79f63e2 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -660,16 +660,15 @@ _PyOS_double_to_string(char *buf, size_t buf_len, double val, /* Supplied precision is unused, must be 0. */ if (precision != 0) return; + /* 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) - return; - precision = 12; - format_code = 'g'; - break; default: assert(0); return; |