Skip to content

Commit 1ff3958

Browse files
eendebakptclaude
andcommitted
gh-NNNN: Route string→float through _Py_fast_float_strtod
Swap the two in-tree _Py_dg_strtod call sites (pystrtod.c's _PyOS_ascii_strtod and floatobject.c's double_round) for _Py_fast_float_strtod. The new shim wraps fast_float::from_chars under the _Py_dg_strtod calling convention. fast_float's default `general` chars_format rejects leading '+' and inf/nan literals. Enable: * allow_leading_plus — _Py_dg_strtod accepts "+3.14"; so does C strtod and test_float_with_comma's assertions depend on it. Leave in place (not enabled): * skip_white_space — _Py_dg_strtod did not skip whitespace and the test_capi test_string_to_double asserts that " 0.1" raises ValueError. Higher-level callers (PyFloat_FromString) strip whitespace before the strtod call. * no_infnan stays ON so pystrtod.c's _Py_parse_inf_or_nan fallback keeps ownership of "inf"/"infinity"/"nan" literal parsing and continues to produce bit-identical results on those inputs. Overflow and underflow map cleanly: fast_float returns `std::errc::result_out_of_range` in both cases, populating `value` with +/-inf on overflow and the nearest representable value on underflow; the shim translates that to errno = ERANGE. Python/dtoa.c is now completely unreachable from the rest of the tree — commit 8 removes it. The full float-formatting + strtod + C-API regression suite (1,501 tests across test_float, test_strtod, test_format, test_capi) passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8433b87 commit 1ff3958

3 files changed

Lines changed: 7 additions & 3 deletions

File tree

Objects/floatobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ double_round(double x, int ndigits) {
940940
/* and convert the resulting string back to a double */
941941
errno = 0;
942942
_Py_SET_53BIT_PRECISION_START;
943-
rounded = _Py_dg_strtod(mybuf, NULL);
943+
rounded = _Py_fast_float_strtod(mybuf, NULL);
944944
_Py_SET_53BIT_PRECISION_END;
945945
if (errno == ERANGE && fabs(rounded) >= 1.)
946946
PyErr_SetString(PyExc_OverflowError,

Python/_fast_float/fast_float_strtod.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ _Py_fast_float_strtod(const char *nptr, char **endptr)
4040
const char *last = nptr + std::strlen(nptr);
4141
double value = 0.0;
4242

43+
// fast_float's `general` default rejects leading '+' and "inf"/"nan".
44+
// _Py_dg_strtod accepted leading '+' (C strtod does too) and left
45+
// "inf"/"nan" to pystrtod.c's _Py_parse_inf_or_nan fallback — match both.
4346
auto result = fast_float::from_chars(
4447
nptr, last, value,
4548
fast_float::chars_format::general
49+
| fast_float::chars_format::allow_leading_plus
4650
| fast_float::chars_format::no_infnan);
4751

4852
if (result.ec == std::errc::invalid_argument) {

Python/pystrtod.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* -*- Mode: C; c-file-style: "python" -*- */
22

33
#include <Python.h>
4-
#include "pycore_dtoa.h" // _Py_dg_strtod()
4+
#include "pycore_dtoa.h" // _Py_fast_float_strtod(), _Py_fmt_dtoa()
55
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
66

77
#include <locale.h> // localeconv()
@@ -101,7 +101,7 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr)
101101
errno = 0;
102102

103103
_Py_SET_53BIT_PRECISION_START;
104-
result = _Py_dg_strtod(nptr, endptr);
104+
result = _Py_fast_float_strtod(nptr, endptr);
105105
_Py_SET_53BIT_PRECISION_END;
106106

107107
if (*endptr == nptr)

0 commit comments

Comments
 (0)