mirror of https://github.com/python/cpython
Issue #5920: Changed format.__float__ and complex.__float__ to use a precision of 12 when using the empty presentation type. This more closely matches str()'s behavior and reduces surprises when adding alignment flags to an empty format string. Patch by Mark Dickinson.
This commit is contained in:
parent
929ab93489
commit
a985a3aee4
|
@ -86,10 +86,10 @@ The following functions provide locale-independent string to number conversions.
|
||||||
Convert a :ctype:`double` *val* to a string using supplied
|
Convert a :ctype:`double` *val* to a string using supplied
|
||||||
*format_code*, *precision*, and *flags*.
|
*format_code*, *precision*, and *flags*.
|
||||||
|
|
||||||
*format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``, ``'g'``,
|
*format_code* must be one of ``'e'``, ``'E'``, ``'f'``, ``'F'``,
|
||||||
``'G'``, ``'s'``, or ``'r'``. For ``'s'`` and ``'r'``, the supplied
|
``'g'``, ``'G'`` or ``'r'``. For ``'r'``, the supplied *precision*
|
||||||
*precision* must be 0 and is ignored. These specify the standard
|
must be 0 and is ignored. The ``'r'`` format code specifies the
|
||||||
:func:`str` and :func:`repr` formats, respectively.
|
standard :func:`repr` format.
|
||||||
|
|
||||||
*flags* can be zero or more of the values *Py_DTSF_SIGN*,
|
*flags* can be zero or more of the values *Py_DTSF_SIGN*,
|
||||||
*Py_DTSF_ADD_DOT_0*, or *Py_DTSF_ALT*, or-ed together:
|
*Py_DTSF_ADD_DOT_0*, or *Py_DTSF_ALT*, or-ed together:
|
||||||
|
|
|
@ -21,6 +21,12 @@ 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
|
||||||
|
|
|
@ -467,6 +467,16 @@ class ComplexTest(unittest.TestCase):
|
||||||
self.assertEqual(format(3+0j, ''), str(3+0j))
|
self.assertEqual(format(3+0j, ''), str(3+0j))
|
||||||
self.assertEqual(format(3.2+0j, ''), str(3.2+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(1+3j, 'g'), '1+3j')
|
||||||
self.assertEqual(format(3j, 'g'), '0+3j')
|
self.assertEqual(format(3j, 'g'), '0+3j')
|
||||||
self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j')
|
self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j')
|
||||||
|
|
|
@ -257,6 +257,53 @@ class IEEEFormatTestCase(unittest.TestCase):
|
||||||
self.assertEquals(math.atan2(float('-1e-1000'), -1),
|
self.assertEquals(math.atan2(float('-1e-1000'), -1),
|
||||||
math.atan2(-0.0, -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"),
|
@unittest.skipUnless(float.__getformat__("double").startswith("IEEE"),
|
||||||
"test requires IEEE 754 doubles")
|
"test requires IEEE 754 doubles")
|
||||||
def test_format_testfile(self):
|
def test_format_testfile(self):
|
||||||
|
|
|
@ -12,6 +12,15 @@ What's New in Python 2.7 alpha 1
|
||||||
Core and Builtins
|
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
|
- Issue #5890: in subclasses of 'property' the __doc__ attribute was
|
||||||
shadowed by classtype's, even if it was None. property now
|
shadowed by classtype's, even if it was None. property now
|
||||||
inserts the __doc__ into the subclass instance __dict__.
|
inserts the __doc__ into the subclass instance __dict__.
|
||||||
|
|
|
@ -354,7 +354,7 @@ complex_dealloc(PyObject *op)
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
complex_format(PyComplexObject *v, char format_code)
|
complex_format(PyComplexObject *v, int precision, char format_code)
|
||||||
{
|
{
|
||||||
PyObject *result = NULL;
|
PyObject *result = NULL;
|
||||||
Py_ssize_t len;
|
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) {
|
if (v->cval.real == 0. && copysign(1.0, v->cval.real)==1.0) {
|
||||||
re = "";
|
re = "";
|
||||||
im = PyOS_double_to_string(v->cval.imag, format_code,
|
im = PyOS_double_to_string(v->cval.imag, format_code,
|
||||||
0, 0, NULL);
|
precision, 0, NULL);
|
||||||
if (!im) {
|
if (!im) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -382,7 +382,7 @@ complex_format(PyComplexObject *v, char format_code)
|
||||||
} else {
|
} else {
|
||||||
/* Format imaginary part with sign, real part without */
|
/* Format imaginary part with sign, real part without */
|
||||||
pre = PyOS_double_to_string(v->cval.real, format_code,
|
pre = PyOS_double_to_string(v->cval.real, format_code,
|
||||||
0, 0, NULL);
|
precision, 0, NULL);
|
||||||
if (!pre) {
|
if (!pre) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -390,7 +390,7 @@ complex_format(PyComplexObject *v, char format_code)
|
||||||
re = pre;
|
re = pre;
|
||||||
|
|
||||||
im = PyOS_double_to_string(v->cval.imag, format_code,
|
im = PyOS_double_to_string(v->cval.imag, format_code,
|
||||||
0, Py_DTSF_SIGN, NULL);
|
precision, Py_DTSF_SIGN, NULL);
|
||||||
if (!im) {
|
if (!im) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -421,7 +421,10 @@ complex_print(PyComplexObject *v, FILE *fp, int flags)
|
||||||
{
|
{
|
||||||
PyObject *formatv;
|
PyObject *formatv;
|
||||||
char *buf;
|
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)
|
if (formatv == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
buf = PyString_AS_STRING(formatv);
|
buf = PyString_AS_STRING(formatv);
|
||||||
|
@ -435,13 +438,13 @@ complex_print(PyComplexObject *v, FILE *fp, int flags)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
complex_repr(PyComplexObject *v)
|
complex_repr(PyComplexObject *v)
|
||||||
{
|
{
|
||||||
return complex_format(v, 'r');
|
return complex_format(v, 0, 'r');
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
complex_str(PyComplexObject *v)
|
complex_str(PyComplexObject *v)
|
||||||
{
|
{
|
||||||
return complex_format(v, 's');
|
return complex_format(v, PyFloat_STR_PRECISION, 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
|
|
|
@ -352,7 +352,7 @@ convert_to_double(PyObject **v, double *dbl)
|
||||||
void
|
void
|
||||||
PyFloat_AsString(char *buf, PyFloatObject *v)
|
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);
|
Py_DTSF_ADD_DOT_0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,9 +368,13 @@ static int
|
||||||
float_print(PyFloatObject *v, FILE *fp, int flags)
|
float_print(PyFloatObject *v, FILE *fp, int flags)
|
||||||
{
|
{
|
||||||
char buf[100];
|
char buf[100];
|
||||||
_PyOS_double_to_string(buf, sizeof(buf), v->ob_fval,
|
if (flags & Py_PRINT_RAW)
|
||||||
(flags & Py_PRINT_RAW) ? 's' : 'r',
|
_PyOS_double_to_string(buf, sizeof(buf), v->ob_fval,
|
||||||
0, Py_DTSF_ADD_DOT_0, NULL);
|
'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
|
Py_BEGIN_ALLOW_THREADS
|
||||||
fputs(buf, fp);
|
fputs(buf, fp);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
@ -390,7 +394,8 @@ static PyObject *
|
||||||
float_str(PyFloatObject *v)
|
float_str(PyFloatObject *v)
|
||||||
{
|
{
|
||||||
char buf[100];
|
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);
|
Py_DTSF_ADD_DOT_0, NULL);
|
||||||
return PyString_FromString(buf);
|
return PyString_FromString(buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -881,6 +881,7 @@ format_float_internal(PyObject *value,
|
||||||
int has_decimal;
|
int has_decimal;
|
||||||
double val;
|
double val;
|
||||||
Py_ssize_t precision = format->precision;
|
Py_ssize_t precision = format->precision;
|
||||||
|
Py_ssize_t default_precision = 6;
|
||||||
STRINGLIB_CHAR type = format->type;
|
STRINGLIB_CHAR type = format->type;
|
||||||
int add_pct = 0;
|
int add_pct = 0;
|
||||||
STRINGLIB_CHAR *p;
|
STRINGLIB_CHAR *p;
|
||||||
|
@ -907,9 +908,10 @@ format_float_internal(PyObject *value,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == '\0') {
|
if (type == '\0') {
|
||||||
/* Omitted type specifier. This is like 'g' but with at least
|
/* Omitted type specifier. This is like 'g' but with at least one
|
||||||
one digit after the decimal point. */
|
digit after the decimal point, and different default precision.*/
|
||||||
type = 'g';
|
type = 'g';
|
||||||
|
default_precision = PyFloat_STR_PRECISION;
|
||||||
flags |= Py_DTSF_ADD_DOT_0;
|
flags |= Py_DTSF_ADD_DOT_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,7 +935,7 @@ format_float_internal(PyObject *value,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (precision < 0)
|
if (precision < 0)
|
||||||
precision = 6;
|
precision = default_precision;
|
||||||
|
|
||||||
#if PY_VERSION_HEX < 0x03010000
|
#if PY_VERSION_HEX < 0x03010000
|
||||||
/* 3.1 no longer converts large 'f' to 'g'. */
|
/* 3.1 no longer converts large 'f' to 'g'. */
|
||||||
|
@ -1039,6 +1041,7 @@ format_complex_internal(PyObject *value,
|
||||||
int re_has_decimal;
|
int re_has_decimal;
|
||||||
int im_has_decimal;
|
int im_has_decimal;
|
||||||
Py_ssize_t precision = format->precision;
|
Py_ssize_t precision = format->precision;
|
||||||
|
Py_ssize_t default_precision = 6;
|
||||||
STRINGLIB_CHAR type = format->type;
|
STRINGLIB_CHAR type = format->type;
|
||||||
STRINGLIB_CHAR *p_re;
|
STRINGLIB_CHAR *p_re;
|
||||||
STRINGLIB_CHAR *p_im;
|
STRINGLIB_CHAR *p_im;
|
||||||
|
@ -1100,6 +1103,7 @@ 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 = 'g';
|
||||||
|
default_precision = PyFloat_STR_PRECISION;
|
||||||
add_parens = 1;
|
add_parens = 1;
|
||||||
if (re == 0.0)
|
if (re == 0.0)
|
||||||
skip_re = 1;
|
skip_re = 1;
|
||||||
|
@ -1115,7 +1119,7 @@ format_complex_internal(PyObject *value,
|
||||||
type = 'f';
|
type = 'f';
|
||||||
|
|
||||||
if (precision < 0)
|
if (precision < 0)
|
||||||
precision = 6;
|
precision = default_precision;
|
||||||
|
|
||||||
/* 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"
|
||||||
|
|
|
@ -660,16 +660,15 @@ _PyOS_double_to_string(char *buf, size_t buf_len, double val,
|
||||||
/* Supplied precision is unused, must be 0. */
|
/* Supplied precision is unused, must be 0. */
|
||||||
if (precision != 0)
|
if (precision != 0)
|
||||||
return;
|
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;
|
precision = 17;
|
||||||
format_code = 'g';
|
format_code = 'g';
|
||||||
break;
|
break;
|
||||||
case 's': /* str format */
|
|
||||||
/* Supplied precision is unused, must be 0. */
|
|
||||||
if (precision != 0)
|
|
||||||
return;
|
|
||||||
precision = 12;
|
|
||||||
format_code = 'g';
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue