]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #5920: Changed format.__float__ and complex.__float__ to use a precision of...
authorEric Smith <eric@trueblade.com>
Tue, 5 May 2009 14:04:18 +0000 (14:04 +0000)
committerEric Smith <eric@trueblade.com>
Tue, 5 May 2009 14:04:18 +0000 (14:04 +0000)
Doc/c-api/conversion.rst
Include/floatobject.h
Lib/test/test_complex.py
Lib/test/test_float.py
Misc/NEWS
Objects/complexobject.c
Objects/floatobject.c
Objects/stringlib/formatter.h
Python/pystrtod.c

index 403c1837373c1c2c02187d5061fa97c79e3e16a8..318842c32f7b9bfcfeb887cdb57fb7a9356c305d 100644 (file)
@@ -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:
index 364b913b47275631c5b79b91eca89baf02cb82a6..5b8d1a1b16d5087d44c6f14fe8d3dfe61bf5cd85 100644 (file)
@@ -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
index ac193536424c70a320057206100e6f6da2bc4f65..1593f7bce4bbbb66121401ec8f5ad683c8251fb7 100644 (file)
@@ -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')
index fb6daafc0f3f0aa0ea33ceba4665a96f67bb1d09..b617fa37023506775958876574c8f325cef07a48 100644 (file)
@@ -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')
 
index d388be1e6dd588cdcddcd39daa43ba85a25ca098..dbd10b283f064872d708fbccaa0cc0eaba96ea81 100644 (file)
--- 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
index 8fdbd37d0b6bd9023c5c922e32c275ff9925e2a1..0d13edfd2e74587c4a9f9fbc14f95a7f6c865ab4 100644 (file)
@@ -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
index 1071fae99f1a0f514217c360c6db311592b7d94f..1074f3d0fd5f67572b7bf250cca643d532967284 100644 (file)
@@ -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,
index 1f3c535b4ae759e87dcb0641fd17fbae62a27d30..3b2218128dd98e6589db0ffc15a5b7cd30f9c8d3 100644 (file)
@@ -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"
index a50d36046a0a8d5e3d0a2bf967b97c97ec7a21e2..d36f9310882808a50aee44116365f8c0c1f0009a 100644 (file)
@@ -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;