Issue #9337: Make float.__str__ identical to float.__repr__.

(And similarly for complex numbers.)
This commit is contained in:
Mark Dickinson 2010-08-04 20:56:28 +00:00
parent b6c5074920
commit 388122d43b
10 changed files with 63 additions and 85 deletions

View File

@ -92,18 +92,17 @@ thing in all languages that support your hardware's floating-point arithmetic
(although some languages may not *display* the difference by default, or in all (although some languages may not *display* the difference by default, or in all
output modes). output modes).
Python's built-in :func:`str` function produces only 12 significant digits, and For more pleasant output, you may may wish to use string formatting to produce a limited number of significant digits::
you may wish to use that instead. It's unusual for ``eval(str(x))`` to
reproduce *x*, but the output may be more pleasant to look at::
>>> str(math.pi) >>> format(math.pi, '.12g') # give 12 significant digits
'3.14159265359' '3.14159265359'
>>> format(math.pi, '.2f') # give 2 digits after the point
'3.14'
>>> repr(math.pi) >>> repr(math.pi)
'3.141592653589793' '3.141592653589793'
>>> format(math.pi, '.2f')
'3.14'
It's important to realize that this is, in a real sense, an illusion: you're It's important to realize that this is, in a real sense, an illusion: you're
simply rounding the *display* of the true machine value. simply rounding the *display* of the true machine value.

View File

@ -21,12 +21,6 @@ PyAPI_DATA(PyTypeObject) PyFloat_Type;
#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) #define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type)
#define PyFloat_CheckExact(op) (Py_TYPE(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 #ifdef Py_NAN
#define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN) #define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN)
#endif #endif

View File

@ -314,43 +314,37 @@
%#.5g 234.56 -> 234.56 %#.5g 234.56 -> 234.56
%#.6g 234.56 -> 234.560 %#.6g 234.56 -> 234.560
-- for repr formatting see the separate test_short_repr test in -- repr formatting. Result always includes decimal point and at
-- test_float.py. Not all platforms use short repr for floats.
-- str formatting. Result always includes decimal point and at
-- least one digit after the point, or an exponent. -- least one digit after the point, or an exponent.
%s 0 -> 0.0 %r 0 -> 0.0
%s 1 -> 1.0 %r 1 -> 1.0
%s 0.01 -> 0.01 %r 0.01 -> 0.01
%s 0.02 -> 0.02 %r 0.02 -> 0.02
%s 0.03 -> 0.03 %r 0.03 -> 0.03
%s 0.04 -> 0.04 %r 0.04 -> 0.04
%s 0.05 -> 0.05 %r 0.05 -> 0.05
-- str truncates to 12 significant digits -- values >= 1e16 get an exponent
%s 1.234123412341 -> 1.23412341234 %r 10 -> 10.0
%s 1.23412341234 -> 1.23412341234 %r 100 -> 100.0
%s 1.2341234123 -> 1.2341234123 %r 1e15 -> 1000000000000000.0
%r 9.999e15 -> 9999000000000000.0
-- values >= 1e11 get an exponent %r 9999999999999998 -> 9999999999999998.0
%s 10 -> 10.0 %r 9999999999999999 -> 1e+16
%s 100 -> 100.0 %r 1e16 -> 1e+16
%s 1e10 -> 10000000000.0 %r 1e17 -> 1e+17
%s 9.999e10 -> 99990000000.0
%s 99999999999 -> 99999999999.0
%s 99999999999.9 -> 99999999999.9
%s 99999999999.99 -> 1e+11
%s 1e11 -> 1e+11
%s 1e12 -> 1e+12
-- as do values < 1e-4 -- as do values < 1e-4
%s 1e-3 -> 0.001 %r 1e-3 -> 0.001
%s 1.001e-4 -> 0.0001001 %r 1.001e-4 -> 0.0001001
%s 1.000000000001e-4 -> 0.0001 %r 1.0000000000000001e-4 -> 0.0001
%s 1.00000000001e-4 -> 0.000100000000001 %r 1.000000000000001e-4 -> 0.0001000000000000001
%s 1.0000000001e-4 -> 0.00010000000001 %r 1.00000000001e-4 -> 0.000100000000001
%s 1e-4 -> 0.0001 %r 1.0000000001e-4 -> 0.00010000000001
%s 0.999999999999e-4 -> 9.99999999999e-05 %r 1e-4 -> 0.0001
%s 0.999e-4 -> 9.99e-05 %r 0.99999999999999999e-4 -> 0.0001
%s 1e-5 -> 1e-05 %r 0.9999999999999999e-4 -> 9.999999999999999e-05
%r 0.999999999999e-4 -> 9.99999999999e-05
%r 0.999e-4 -> 9.99e-05
%r 1e-5 -> 1e-05

View File

@ -617,7 +617,9 @@ class ReprTestCase(unittest.TestCase):
negs = '-'+s negs = '-'+s
self.assertEqual(s, repr(float(s))) self.assertEqual(s, repr(float(s)))
self.assertEqual(negs, repr(float(negs))) self.assertEqual(negs, repr(float(negs)))
# Since Python 3.2, repr and str are identical
self.assertEqual(repr(float(s)), str(float(s)))
self.assertEqual(repr(float(negs)), str(float(negs)))
@requires_IEEE_754 @requires_IEEE_754
class RoundTestCase(unittest.TestCase): class RoundTestCase(unittest.TestCase):

View File

@ -598,11 +598,11 @@ def decistmt(s):
The format of the exponent is inherited from the platform C library. The format of the exponent is inherited from the platform C library.
Known cases are "e-007" (Windows) and "e-07" (not Windows). Since Known cases are "e-007" (Windows) and "e-07" (not Windows). Since
we're only showing 12 digits, and the 13th isn't close to 5, the we're only showing 11 digits, and the 12th isn't close to 5, the
rest of the output should be platform-independent. rest of the output should be platform-independent.
>>> exec(s) #doctest: +ELLIPSIS >>> exec(s) #doctest: +ELLIPSIS
-3.21716034272e-0...7 -3.2171603427...e-0...7
Output from calculations with Decimal should be identical across all Output from calculations with Decimal should be identical across all
platforms. platforms.

View File

@ -80,8 +80,7 @@ class UnicodeDatabaseTest(unittest.TestCase):
class UnicodeFunctionsTest(UnicodeDatabaseTest): class UnicodeFunctionsTest(UnicodeDatabaseTest):
# update this, if the database changes # update this, if the database changes
expectedchecksum = '6ccf1b1a36460d2694f9b0b0f0324942fe70ede6' expectedchecksum = 'e89a6380093a00a7685ac7b92e7367d737fcb79b'
def test_function_checksum(self): def test_function_checksum(self):
data = [] data = []
h = hashlib.sha1() h = hashlib.sha1()
@ -90,9 +89,9 @@ class UnicodeFunctionsTest(UnicodeDatabaseTest):
char = chr(i) char = chr(i)
data = [ data = [
# Properties # Properties
str(self.db.digit(char, -1)), format(self.db.digit(char, -1), '.12g'),
str(self.db.numeric(char, -1)), format(self.db.numeric(char, -1), '.12g'),
str(self.db.decimal(char, -1)), format(self.db.decimal(char, -1), '.12g'),
self.db.category(char), self.db.category(char),
self.db.bidirectional(char), self.db.bidirectional(char),
self.db.decomposition(char), self.db.decomposition(char),

View File

@ -12,6 +12,9 @@ What's New in Python 3.2 Alpha 2?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #9337: The str() of a float or complex number is now identical
to its repr().
- Issue #9416: Fix some issues with complex formatting where the - Issue #9416: Fix some issues with complex formatting where the
output with no type specifier failed to match the str output: output with no type specifier failed to match the str output:

View File

@ -394,12 +394,6 @@ complex_repr(PyComplexObject *v)
return complex_format(v, 0, 'r'); return complex_format(v, 0, 'r');
} }
static PyObject *
complex_str(PyComplexObject *v)
{
return complex_format(v, PyFloat_STR_PRECISION, 'g');
}
static long static long
complex_hash(PyComplexObject *v) complex_hash(PyComplexObject *v)
{ {
@ -1104,7 +1098,7 @@ PyTypeObject PyComplex_Type = {
0, /* tp_as_mapping */ 0, /* tp_as_mapping */
(hashfunc)complex_hash, /* tp_hash */ (hashfunc)complex_hash, /* tp_hash */
0, /* tp_call */ 0, /* tp_call */
(reprfunc)complex_str, /* tp_str */ (reprfunc)complex_repr, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */

View File

@ -305,32 +305,20 @@ convert_to_double(PyObject **v, double *dbl)
} }
static PyObject * static PyObject *
float_str_or_repr(PyFloatObject *v, int precision, char format_code) float_repr(PyFloatObject *v)
{ {
PyObject *result; PyObject *result;
char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v), char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
format_code, precision, 'r', 0,
Py_DTSF_ADD_DOT_0, Py_DTSF_ADD_DOT_0,
NULL); NULL);
if (!buf) if (!buf)
return PyErr_NoMemory(); return PyErr_NoMemory();
result = PyUnicode_FromString(buf); result = PyUnicode_FromString(buf);
PyMem_Free(buf); PyMem_Free(buf);
return result; return result;
} }
static PyObject *
float_repr(PyFloatObject *v)
{
return float_str_or_repr(v, 0, 'r');
}
static PyObject *
float_str(PyFloatObject *v)
{
return float_str_or_repr(v, PyFloat_STR_PRECISION, 'g');
}
/* Comparison is pretty much a nightmare. When comparing float to float, /* Comparison is pretty much a nightmare. When comparing float to float,
* we do it as straightforwardly (and long-windedly) as conceivable, so * we do it as straightforwardly (and long-windedly) as conceivable, so
* that, e.g., Python x == y delivers the same result as the platform * that, e.g., Python x == y delivers the same result as the platform
@ -1169,7 +1157,7 @@ float_hex(PyObject *v)
CONVERT_TO_DOUBLE(v, x); CONVERT_TO_DOUBLE(v, x);
if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) if (Py_IS_NAN(x) || Py_IS_INFINITY(x))
return float_str((PyFloatObject *)v); return float_repr((PyFloatObject *)v);
if (x == 0.0) { if (x == 0.0) {
if (copysign(1.0, x) == -1.0) if (copysign(1.0, x) == -1.0)
@ -1873,7 +1861,7 @@ PyTypeObject PyFloat_Type = {
0, /* tp_as_mapping */ 0, /* tp_as_mapping */
(hashfunc)float_hash, /* tp_hash */ (hashfunc)float_hash, /* tp_hash */
0, /* tp_call */ 0, /* tp_call */
(reprfunc)float_str, /* tp_str */ (reprfunc)float_repr, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */

View File

@ -950,11 +950,12 @@ format_float_internal(PyObject *value,
} }
if (type == '\0') { if (type == '\0') {
/* Omitted type specifier. This is like 'g' but with at least one /* Omitted type specifier. Behaves in the same way as repr(x)
digit after the decimal point, and different default precision.*/ and str(x) if no precision is given, else like 'g', but with
type = 'g'; at least one digit after the decimal point. */
default_precision = PyFloat_STR_PRECISION;
flags |= Py_DTSF_ADD_DOT_0; flags |= Py_DTSF_ADD_DOT_0;
type = 'r';
default_precision = 0;
} }
if (type == 'n') if (type == 'n')
@ -974,6 +975,8 @@ format_float_internal(PyObject *value,
if (precision < 0) if (precision < 0)
precision = default_precision; precision = default_precision;
else if (type == 'r')
type = 'g';
/* Cast "type", because if we're in unicode we need to pass a /* 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" 8-bit char. This is safe, because we've restricted what "type"
@ -1134,8 +1137,8 @@ format_complex_internal(PyObject *value,
if (type == '\0') { if (type == '\0') {
/* Omitted type specifier. Should be like str(self). */ /* Omitted type specifier. Should be like str(self). */
type = 'g'; type = 'r';
default_precision = PyFloat_STR_PRECISION; default_precision = 0;
if (re == 0.0 && copysign(1.0, re) == 1.0) if (re == 0.0 && copysign(1.0, re) == 1.0)
skip_re = 1; skip_re = 1;
else else
@ -1149,6 +1152,8 @@ format_complex_internal(PyObject *value,
if (precision < 0) if (precision < 0)
precision = default_precision; precision = default_precision;
else if (type == 'r')
type = 'g';
/* Cast "type", because if we're in unicode we need to pass a /* 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" 8-bit char. This is safe, because we've restricted what "type"