summaryrefslogtreecommitdiffstats
path: root/Python/pystrtod.c
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-05-01 11:42:00 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2009-05-01 11:42:00 (GMT)
commitf489caf5daa2b0f3a1bd951b585c834aab1a54c6 (patch)
treef57e0339acc0f5b702743d438738ad2f0d2aeba7 /Python/pystrtod.c
parentfb526ac34af13116733fcd0a306016e253d90c08 (diff)
downloadcpython-f489caf5daa2b0f3a1bd951b585c834aab1a54c6.zip
cpython-f489caf5daa2b0f3a1bd951b585c834aab1a54c6.tar.gz
cpython-f489caf5daa2b0f3a1bd951b585c834aab1a54c6.tar.bz2
Issue #5859: Remove use of fixed-length buffers for float formatting
in unicodeobject.c and the fallback version of PyOS_double_to_string. As a result, operations like '%.120e' % 12.34 no longer raise an exception.
Diffstat (limited to 'Python/pystrtod.c')
-rw-r--r--Python/pystrtod.c94
1 files changed, 69 insertions, 25 deletions
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index e68f5d7..1040610 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -620,12 +620,10 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
int flags,
int *type)
{
- char buf[128];
char format[32];
- Py_ssize_t len;
- char *result;
- char *p;
- int t;
+ Py_ssize_t bufsize;
+ char *buf;
+ int t, exp;
int upper = 0;
/* Validate format_code, and map upper and lower case */
@@ -669,6 +667,61 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
return NULL;
}
+ /* Here's a quick-and-dirty calculation to figure out how big a buffer
+ we need. In general, for a finite float we need:
+
+ 1 byte for each digit of the decimal significand, and
+
+ 1 for a possible sign
+ 1 for a possible decimal point
+ 2 for a possible [eE][+-]
+ 1 for each digit of the exponent; if we allow 19 digits
+ total then we're safe up to exponents of 2**63.
+ 1 for the trailing nul byte
+
+ This gives a total of 24 + the number of digits in the significand,
+ and the number of digits in the significand is:
+
+ for 'g' format: at most precision, except possibly
+ when precision == 0, when it's 1.
+ for 'e' format: precision+1
+ for 'f' format: precision digits after the point, at least 1
+ before. To figure out how many digits appear before the point
+ we have to examine the size of the number. If fabs(val) < 1.0
+ then there will be only one digit before the point. If
+ fabs(val) >= 1.0, then there are at most
+
+ 1+floor(log10(ceiling(fabs(val))))
+
+ digits before the point (where the 'ceiling' allows for the
+ possibility that the rounding rounds the integer part of val
+ up). A safe upper bound for the above quantity is
+ 1+floor(exp/3), where exp is the unique integer such that 0.5
+ <= fabs(val)/2**exp < 1.0. This exp can be obtained from
+ frexp.
+
+ So we allow room for precision+1 digits for all formats, plus an
+ extra floor(exp/3) digits for 'f' format.
+
+ */
+
+ if (Py_IS_NAN(val) || Py_IS_INFINITY(val))
+ /* 3 for 'inf'/'nan', 1 for sign, 1 for '\0' */
+ bufsize = 5;
+ else {
+ bufsize = 25 + precision;
+ if (format_code == 'f' && fabs(val) >= 1.0) {
+ frexp(val, &exp);
+ bufsize += exp/3;
+ }
+ }
+
+ buf = PyMem_Malloc(bufsize);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
/* Handle nan and inf. */
if (Py_IS_NAN(val)) {
strcpy(buf, "nan");
@@ -687,38 +740,29 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
PyOS_snprintf(format, sizeof(format), "%%%s.%i%c",
(flags & Py_DTSF_ALT ? "#" : ""), precision,
format_code);
- _PyOS_ascii_formatd(buf, sizeof(buf), format, val, precision);
- }
-
- len = strlen(buf);
-
- /* Add 1 for the trailing 0 byte.
- Add 1 because we might need to make room for the sign.
- */
- result = PyMem_Malloc(len + 2);
- if (result == NULL) {
- PyErr_NoMemory();
- return NULL;
+ _PyOS_ascii_formatd(buf, bufsize, format, val, precision);
}
- p = result;
/* Add sign when requested. It's convenient (esp. when formatting
complex numbers) to include a sign even for inf and nan. */
- if (flags & Py_DTSF_SIGN && buf[0] != '-')
- *p++ = '+';
-
- strcpy(p, buf);
-
+ if (flags & Py_DTSF_SIGN && buf[0] != '-') {
+ size_t len = strlen(buf);
+ /* the bufsize calculations above should ensure that we've got
+ space to add a sign */
+ assert((size_t)bufsize >= len+2);
+ memmove(buf+1, buf, len+1);
+ buf[0] = '+';
+ }
if (upper) {
/* Convert to upper case. */
char *p1;
- for (p1 = p; *p1; p1++)
+ for (p1 = buf; *p1; p1++)
*p1 = Py_TOUPPER(*p1);
}
if (type)
*type = t;
- return result;
+ return buf;
}
#else