Issue #9337: Make float.__str__ identical to float.__repr__.
(And similarly for complex numbers.)
This commit is contained in:
parent
b6c5074920
commit
388122d43b
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue