Skip to content

Commit ed70266

Browse files
eendebakptclaude
andcommitted
gh-NNNN: Route float→string through _Py_fmt_dtoa
Swap the _Py_dg_dtoa / _Py_dg_freedtoa call pair at the three remaining in-tree call sites (pystrtod.c's format_float_short and floatobject.c's double_round) for _Py_fmt_dtoa / _Py_fmt_dtoa_free. Covers all three cpython dtoa modes bit-exactly: * mode 0 (shortest / repr): fmt::detail::dragonbox::to_decimal * mode 2 (N sig digits): fmt::detail::format_float, specs=exp * mode 3 (N frac digits): fmt::detail::format_float, specs=fixed fmt's format_float with specs=exp takes `precision` as total-digits-to- emit, not "digits after the leading one" — so for mode 2 N sig digits we pass N directly. For specs=fixed fmt's adjust_precision adds the decade internally and handles negative precision natively, which is exactly what CPython mode 3 with negative ndigits needs. Python/dtoa.c is still in the build for _Py_dg_strtod (commit 6 handles that swap) but _Py_dg_dtoa is now unreferenced. Full float-formatting regression suite passes: 2,239 tests across test_float, test_format, test_fstring, test_strtod, test_json, test_math, test_cmath, test_complex, test_builtin, test_decimal, test_statistics, test_string, test_str, test_reprlib, test_pprint, test_csv, test_struct, test_long. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 57d1c7d commit ed70266

3 files changed

Lines changed: 14 additions & 10 deletions

File tree

Objects/floatobject.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ float___ceil___impl(PyObject *self)
900900

901901
#if _PY_SHORT_FLOAT_REPR == 1
902902
/* version of double_round that uses the correctly-rounded string<->double
903-
conversions from Python/dtoa.c */
903+
conversions from the fmt/wuffs shims in Python/_fmt/ and Python/_wuffs/. */
904904

905905
static PyObject *
906906
double_round(double x, int ndigits) {
@@ -914,7 +914,7 @@ double_round(double x, int ndigits) {
914914

915915
/* round to a decimal string */
916916
_Py_SET_53BIT_PRECISION_START;
917-
buf = _Py_dg_dtoa(x, 3, ndigits, &decpt, &sign, &buf_end);
917+
buf = _Py_fmt_dtoa(x, 3, ndigits, &decpt, &sign, &buf_end);
918918
_Py_SET_53BIT_PRECISION_END;
919919
if (buf == NULL) {
920920
PyErr_NoMemory();
@@ -951,7 +951,7 @@ double_round(double x, int ndigits) {
951951
if (mybuf != shortbuf)
952952
PyMem_Free(mybuf);
953953
exit:
954-
_Py_dg_freedtoa(buf);
954+
_Py_fmt_dtoa_free(buf);
955955
return result;
956956
}
957957

Python/_fmt/fmt_dtoa.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,11 @@ char *_Py_fmt_dtoa(double d, int mode, int ndigits,
103103
bool fixed;
104104
if (mode == 2) {
105105
specs.set_type(fmt::presentation_type::exp);
106-
// max(1, ndigits) significant digits, fmt wants precision = N - 1.
107-
precision = (ndigits < 1 ? 1 : ndigits) - 1;
106+
// fmt::detail::format_float with specs=exp emits exactly `precision`
107+
// digits (no leading-digit adjustment — that's only for specs=fixed,
108+
// where adjust_precision adds the decade). So for N significant
109+
// digits we pass precision = N directly.
110+
precision = (ndigits < 1 ? 1 : ndigits);
108111
fixed = false;
109112
} else {
110113
// Treat any unrecognised mode as 3 (matches _Py_dg_dtoa's fallthrough).

Python/pystrtod.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -980,11 +980,12 @@ format_float_short(double d, char format_code,
980980
Py_ssize_t decpt, digits_len, vdigits_start, vdigits_end;
981981
_Py_SET_53BIT_PRECISION_HEADER;
982982

983-
/* _Py_dg_dtoa returns a digit string (no decimal point or exponent).
984-
Must be matched by a call to _Py_dg_freedtoa. */
983+
/* fmt-backed drop-in for _Py_dg_dtoa. Returns a digit string (no
984+
decimal point or exponent). Must be matched by a call to
985+
_Py_fmt_dtoa_free. Covers all three modes bit-exactly. */
985986
_Py_SET_53BIT_PRECISION_START;
986-
digits = _Py_dg_dtoa(d, mode, precision, &decpt_as_int, &sign,
987-
&digits_end);
987+
digits = _Py_fmt_dtoa(d, mode, precision, &decpt_as_int, &sign,
988+
&digits_end);
988989
_Py_SET_53BIT_PRECISION_END;
989990

990991
decpt = (Py_ssize_t)decpt_as_int;
@@ -1212,7 +1213,7 @@ format_float_short(double d, char format_code,
12121213
assert(p-buf < bufsize);
12131214
}
12141215
if (digits)
1215-
_Py_dg_freedtoa(digits);
1216+
_Py_fmt_dtoa_free(digits);
12161217

12171218
return buf;
12181219
}

0 commit comments

Comments
 (0)