summaryrefslogtreecommitdiffstats
path: root/Python/pystrtod.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pystrtod.c')
-rw-r--r--Python/pystrtod.c217
1 files changed, 79 insertions, 138 deletions
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index 94dc481..29d7996 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -22,43 +22,6 @@ case_insensitive_match(const char *s, const char *t)
the successfully parsed portion of the string. On failure, return -1.0 and
set *endptr to point to the start of the string. */
-#ifndef PY_NO_SHORT_FLOAT_REPR
-
-double
-_Py_parse_inf_or_nan(const char *p, char **endptr)
-{
- double retval;
- const char *s;
- int negate = 0;
-
- s = p;
- if (*s == '-') {
- negate = 1;
- s++;
- }
- else if (*s == '+') {
- s++;
- }
- if (case_insensitive_match(s, "inf")) {
- s += 3;
- if (case_insensitive_match(s, "inity"))
- s += 5;
- retval = _Py_dg_infinity(negate);
- }
- else if (case_insensitive_match(s, "nan")) {
- s += 3;
- retval = _Py_dg_stdnan(negate);
- }
- else {
- s = p;
- retval = -1.0;
- }
- *endptr = (char *)s;
- return retval;
-}
-
-#else
-
double
_Py_parse_inf_or_nan(const char *p, char **endptr)
{
@@ -94,10 +57,8 @@ _Py_parse_inf_or_nan(const char *p, char **endptr)
return retval;
}
-#endif
-
/**
- * _PyOS_ascii_strtod:
+ * PyOS_ascii_strtod:
* @nptr: the string to convert to a numeric value.
* @endptr: if non-%NULL, it returns the character after
* the last character used in the conversion.
@@ -127,7 +88,7 @@ _Py_parse_inf_or_nan(const char *p, char **endptr)
#ifndef PY_NO_SHORT_FLOAT_REPR
-static double
+double
_PyOS_ascii_strtod(const char *nptr, char **endptr)
{
double result;
@@ -160,11 +121,11 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr)
correctly rounded results.
*/
-static double
+double
_PyOS_ascii_strtod(const char *nptr, char **endptr)
{
char *fail_pos;
- double val;
+ double val = -1.0;
struct lconv *locale_data;
const char *decimal_point;
size_t decimal_point_len;
@@ -309,10 +270,48 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr)
#endif
-/* PyOS_string_to_double converts a null-terminated byte string s (interpreted
- as a string of ASCII characters) to a float. The string should not have
- leading or trailing whitespace. The conversion is independent of the
- current locale.
+/* PyOS_ascii_strtod is DEPRECATED in Python 2.7 and 3.1 */
+
+double
+PyOS_ascii_strtod(const char *nptr, char **endptr)
+{
+ char *fail_pos;
+ const char *p;
+ double x;
+
+ if (PyErr_WarnEx(PyExc_DeprecationWarning,
+ "PyOS_ascii_strtod and PyOS_ascii_atof are "
+ "deprecated. Use PyOS_string_to_double "
+ "instead.", 1) < 0)
+ return -1.0;
+
+ /* _PyOS_ascii_strtod already does everything that we want,
+ except that it doesn't parse leading whitespace */
+ p = nptr;
+ while (Py_ISSPACE(*p))
+ p++;
+ x = _PyOS_ascii_strtod(p, &fail_pos);
+ if (fail_pos == p)
+ fail_pos = (char *)nptr;
+ if (endptr)
+ *endptr = (char *)fail_pos;
+ return x;
+}
+
+/* PyOS_ascii_strtod is DEPRECATED in Python 2.7 and 3.1 */
+
+double
+PyOS_ascii_atof(const char *nptr)
+{
+ return PyOS_ascii_strtod(nptr, NULL);
+}
+
+/* PyOS_string_to_double is the recommended replacement for the deprecated
+ PyOS_ascii_strtod and PyOS_ascii_atof functions. It converts a
+ null-terminated byte string s (interpreted as a string of ASCII characters)
+ to a float. The string should not have leading or trailing whitespace (in
+ contrast, PyOS_ascii_strtod allows leading whitespace but not trailing
+ whitespace). The conversion is independent of the current locale.
If endptr is NULL, try to convert the whole string. Raise ValueError and
return -1.0 if the string is not a valid representation of a floating-point
@@ -342,7 +341,9 @@ PyOS_string_to_double(const char *s,
char *fail_pos;
errno = 0;
+ PyFPE_START_PROTECT("PyOS_string_to_double", return -1.0)
x = _PyOS_ascii_strtod(s, &fail_pos);
+ PyFPE_END_PROTECT(x)
if (errno == ENOMEM) {
PyErr_NoMemory();
@@ -351,15 +352,15 @@ PyOS_string_to_double(const char *s,
else if (!endptr && (fail_pos == s || *fail_pos != '\0'))
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
- "'%.200s'", s);
+ "%.200s", s);
else if (fail_pos == s)
PyErr_Format(PyExc_ValueError,
"could not convert string to float: "
- "'%.200s'", s);
+ "%.200s", s);
else if (errno == ERANGE && fabs(x) >= 1.0 && overflow_exception)
PyErr_Format(overflow_exception,
"value too large to convert to float: "
- "'%.200s'", s);
+ "%.200s", s);
else
result = x;
@@ -368,79 +369,6 @@ PyOS_string_to_double(const char *s,
return result;
}
-/* Remove underscores that follow the underscore placement rule from
- the string and then call the `innerfunc` function on the result.
- It should return a new object or NULL on exception.
-
- `what` is used for the error message emitted when underscores are detected
- that don't follow the rule. `arg` is an opaque pointer passed to the inner
- function.
-
- This is used to implement underscore-agnostic conversion for floats
- and complex numbers.
-*/
-PyObject *
-_Py_string_to_number_with_underscores(
- const char *s, Py_ssize_t orig_len, const char *what, PyObject *obj, void *arg,
- PyObject *(*innerfunc)(const char *, Py_ssize_t, void *))
-{
- char prev;
- const char *p, *last;
- char *dup, *end;
- PyObject *result;
-
- assert(s[orig_len] == '\0');
-
- if (strchr(s, '_') == NULL) {
- return innerfunc(s, orig_len, arg);
- }
-
- dup = PyMem_Malloc(orig_len + 1);
- if (dup == NULL) {
- return PyErr_NoMemory();
- }
- end = dup;
- prev = '\0';
- last = s + orig_len;
- for (p = s; *p; p++) {
- if (*p == '_') {
- /* Underscores are only allowed after digits. */
- if (!(prev >= '0' && prev <= '9')) {
- goto error;
- }
- }
- else {
- *end++ = *p;
- /* Underscores are only allowed before digits. */
- if (prev == '_' && !(*p >= '0' && *p <= '9')) {
- goto error;
- }
- }
- prev = *p;
- }
- /* Underscores are not allowed at the end. */
- if (prev == '_') {
- goto error;
- }
- /* No embedded NULs allowed. */
- if (p != last) {
- goto error;
- }
- *end = '\0';
- result = innerfunc(dup, end - dup, arg);
- PyMem_Free(dup);
- return result;
-
- error:
- PyMem_Free(dup);
- PyErr_Format(PyExc_ValueError,
- "could not convert string to %s: "
- "%R", what, obj);
- return NULL;
-}
-
-#ifdef PY_NO_SHORT_FLOAT_REPR
-
/* Given a string that may have a decimal point in the current
locale, change it back to a dot. Since the string cannot get
longer, no need for a maximum buffer size parameter. */
@@ -600,8 +528,7 @@ Py_LOCAL_INLINE(char *)
ensure_decimal_point(char* buffer, size_t buf_size, int precision)
{
int digit_count, insert_count = 0, convert_to_exp = 0;
- const char *chars_to_insert;
- char *digits_start;
+ char *chars_to_insert, *digits_start;
/* search for the first non-digit character */
char *p = buffer;
@@ -691,13 +618,12 @@ ensure_decimal_point(char* buffer, size_t buf_size, int precision)
#define FLOAT_FORMATBUFLEN 120
/**
- * _PyOS_ascii_formatd:
+ * PyOS_ascii_formatd:
* @buffer: A buffer to place the resulting string in
* @buf_size: The length of the buffer.
* @format: The printf()-style format to use for the
* code to use for converting.
* @d: The #gdouble to convert
- * @precision: The precision to use when formatting.
*
* Converts a #gdouble to a string, using the '.' as
* decimal point. To format the number you pass in
@@ -710,7 +636,7 @@ ensure_decimal_point(char* buffer, size_t buf_size, int precision)
* Return value: The pointer to the buffer with the converted string.
* On failure returns NULL but does not set any Python exception.
**/
-static char *
+char *
_PyOS_ascii_formatd(char *buffer,
size_t buf_size,
const char *format,
@@ -790,9 +716,25 @@ _PyOS_ascii_formatd(char *buffer,
return buffer;
}
+char *
+PyOS_ascii_formatd(char *buffer,
+ size_t buf_size,
+ const char *format,
+ double d)
+{
+ if (PyErr_WarnEx(PyExc_DeprecationWarning,
+ "PyOS_ascii_formatd is deprecated, "
+ "use PyOS_double_to_string instead", 1) < 0)
+ return NULL;
+
+ return _PyOS_ascii_formatd(buffer, buf_size, format, d, -1);
+}
+
+#ifdef PY_NO_SHORT_FLOAT_REPR
+
/* The fallback code to use if _Py_dg_dtoa is not available. */
-char * PyOS_double_to_string(double val,
+PyAPI_FUNC(char *) PyOS_double_to_string(double val,
char format_code,
int precision,
int flags,
@@ -951,12 +893,12 @@ char * PyOS_double_to_string(double val,
#define OFS_E 2
/* The lengths of these are known to the code below, so don't change them */
-static const char * const lc_float_strings[] = {
+static char *lc_float_strings[] = {
"inf",
"nan",
"e",
};
-static const char * const uc_float_strings[] = {
+static char *uc_float_strings[] = {
"INF",
"NAN",
"E",
@@ -993,10 +935,9 @@ static const char * const uc_float_strings[] = {
static char *
format_float_short(double d, char format_code,
- int mode, int precision,
+ int mode, Py_ssize_t precision,
int always_add_sign, int add_dot_0_if_integer,
- int use_alt_formatting, const char * const *float_strings,
- int *type)
+ int use_alt_formatting, char **float_strings, int *type)
{
char *buf = NULL;
char *p = NULL;
@@ -1063,7 +1004,7 @@ format_float_short(double d, char format_code,
else {
/* shouldn't get here: Gay's code should always return
something starting with a digit, an 'I', or 'N' */
- Py_UNREACHABLE();
+ assert(0);
}
goto exit;
}
@@ -1128,7 +1069,7 @@ format_float_short(double d, char format_code,
/* if using an exponent, reset decimal point position to 1 and adjust
exponent accordingly.*/
if (use_exp) {
- exp = (int)decpt - 1;
+ exp = decpt - 1;
decpt = 1;
}
/* ensure vdigits_start < decpt <= vdigits_end, or vdigits_start <
@@ -1239,13 +1180,13 @@ format_float_short(double d, char format_code,
}
-char * PyOS_double_to_string(double val,
+PyAPI_FUNC(char *) PyOS_double_to_string(double val,
char format_code,
int precision,
int flags,
int *type)
{
- const char * const *float_strings = lc_float_strings;
+ char **float_strings = lc_float_strings;
int mode;
/* Validate format_code, and map upper and lower case. Compute the