Skip to content

Commit 9cd5674

Browse files
committed
use snsprintf for longdouble
1 parent 5b69baa commit 9cd5674

2 files changed

Lines changed: 28 additions & 4 deletions

File tree

src/csrc/scalar.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <sleef.h>
33
#include <sleefquad.h>
44
#include <stdlib.h>
5+
#include <float.h>
56

67
#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API
78
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
@@ -620,10 +621,15 @@ QuadPrecision_reduce(QuadPrecisionObject *self, PyObject *Py_UNUSED(ignored))
620621
opt.trim_mode, opt.digits_left, opt.exp_digits);
621622
}
622623
else {
623-
Sleef_quad sleef_val = Sleef_cast_from_doubleq1(self->value.longdouble_value);
624-
str_value = Dragon4_Scientific_QuadDType(&sleef_val, opt.digit_mode, opt.precision,
625-
opt.min_digits, opt.sign, opt.trim_mode,
626-
opt.digits_left, opt.exp_digits);
624+
char buffer[128];
625+
int written = snprintf(buffer, sizeof(buffer), "%.*Le",
626+
LDBL_DECIMAL_DIG - 1, self->value.longdouble_value);
627+
if (written < 0 || written >= (int)sizeof(buffer)) {
628+
PyErr_SetString(PyExc_RuntimeError,
629+
"Failed to format long double for pickle");
630+
return NULL;
631+
}
632+
str_value = PyUnicode_FromString(buffer);
627633
}
628634
if (str_value == NULL) {
629635
return NULL;

tests/test_quaddtype.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5412,6 +5412,24 @@ def test_pickle_scalar_preserves_type(self):
54125412
loaded = pickle.loads(pickle.dumps(QuadPrecision("1.0")))
54135413
assert type(loaded) is QuadPrecision
54145414

5415+
@pytest.mark.parametrize("backend", ["sleef", "longdouble"])
5416+
def test_pickle_scalar_preserves_full_precision(self, backend):
5417+
"""Diagnostic precision-loss test. Two values with the same printed
5418+
repr can still differ at the full bit width, so check via subtraction
5419+
(which preserves precision) — `loaded - original` must be exactly zero.
5420+
Catches the regression where the longdouble path went through (double)."""
5421+
import pickle
5422+
# A value with more than 16 significant digits — exercises precision
5423+
# beyond what double can represent.
5424+
original = QuadPrecision("3.14159265358979323846264338327950288",
5425+
backend=backend)
5426+
loaded = pickle.loads(pickle.dumps(original))
5427+
diff = loaded - original
5428+
assert diff == QuadPrecision("0.0", backend=backend), (
5429+
f"pickle round-trip lost precision on {backend}: "
5430+
f"loaded - original = {diff!r}"
5431+
)
5432+
54155433
def test_pickle_scalar_preserves_backend_across_mix(self):
54165434
"""Each backend pickle must come back as the same backend, not silently
54175435
defaulting to sleef."""

0 commit comments

Comments
 (0)