Skip to content

Commit f52ffdf

Browse files
authored
Merge pull request #197 from SwayamInSync/196
BUG: Fix Round-tripping QuadPrecision through string
2 parents 6fa5535 + ca06e81 commit f52ffdf

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

quaddtype/numpy_quaddtype/src/scalar.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#include "scalar_ops.h"
1616
#include "dragon4.h"
1717

18+
// For IEEE 754 binary128 (quad precision), we need 36 decimal digits
19+
// to guarantee round-trip conversion (string -> parse -> equals original value)
20+
// Formula: ceil(1 + MANT_DIG * log10(2)) = ceil(1 + 113 * 0.30103) = 36
21+
// src: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
22+
#define SLEEF_QUAD_DECIMAL_DIG 36
23+
1824

1925
QuadPrecisionObject *
2026
QuadPrecision_raw_new(QuadBackendType backend)
@@ -152,7 +158,7 @@ QuadPrecision_str_dragon4(QuadPrecisionObject *self)
152158
Dragon4_Options opt = {.scientific = 0,
153159
.digit_mode = DigitMode_Unique,
154160
.cutoff_mode = CutoffMode_TotalLength,
155-
.precision = SLEEF_QUAD_DIG,
161+
.precision = SLEEF_QUAD_DECIMAL_DIG,
156162
.sign = 1,
157163
.trim_mode = TrimMode_LeaveOneZero,
158164
.digits_left = 1,
@@ -203,7 +209,7 @@ QuadPrecision_repr_dragon4(QuadPrecisionObject *self)
203209
Dragon4_Options opt = {.scientific = 1,
204210
.digit_mode = DigitMode_Unique,
205211
.cutoff_mode = CutoffMode_TotalLength,
206-
.precision = SLEEF_QUAD_DIG,
212+
.precision = SLEEF_QUAD_DECIMAL_DIG,
207213
.sign = 1,
208214
.trim_mode = TrimMode_LeaveOneZero,
209215
.digits_left = 1,

quaddtype/tests/test_quaddtype.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,42 @@ def test_create_scalar_simple():
1212
assert isinstance(QuadPrecision(1.63), QuadPrecision)
1313
assert isinstance(QuadPrecision(1), QuadPrecision)
1414

15+
16+
def test_string_roundtrip():
17+
# Test with various values that require full quad precision
18+
test_values = [
19+
QuadPrecision("0.417022004702574000667425480060047"), # Random value
20+
QuadPrecision("1.23456789012345678901234567890123456789"), # Many digits
21+
numpy_quaddtype.pi, # Mathematical constant
22+
numpy_quaddtype.e,
23+
QuadPrecision("1e-100"), # Very small
24+
QuadPrecision("1e100"), # Very large
25+
QuadPrecision("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233"), # very precise pi
26+
]
27+
28+
for original in test_values:
29+
string_repr = str(original)
30+
reconstructed = QuadPrecision(string_repr)
31+
32+
# Values should be exactly equal (bit-for-bit identical)
33+
assert reconstructed == original, (
34+
f"Round-trip failed for {repr(original)}:\n"
35+
f" Original: {repr(original)}\n"
36+
f" String: {string_repr}\n"
37+
f" Reconstructed: {repr(reconstructed)}"
38+
)
39+
40+
# Also verify repr() preserves value
41+
repr_str = repr(original)
42+
# Extract the string value from repr format: QuadPrecision('value', backend='...')
43+
value_from_repr = repr_str.split("'")[1]
44+
reconstructed_from_repr = QuadPrecision(value_from_repr)
45+
46+
assert reconstructed_from_repr == original, (
47+
f"Round-trip from repr() failed for {repr(original)}"
48+
)
49+
50+
1551
@pytest.mark.parametrize("name,expected", [("pi", np.pi), ("e", np.e), ("log2e", np.log2(np.e)), ("log10e", np.log10(np.e)), ("ln2", np.log(2.0)), ("ln10", np.log(10.0))])
1652
def test_math_constant(name, expected):
1753
assert isinstance(getattr(numpy_quaddtype, name), QuadPrecision)

0 commit comments

Comments
 (0)