From ca06e8111c6d77c8da20e248435c0bf74a2aa0ec Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 23 Oct 2025 09:05:25 +0530 Subject: [PATCH] fixed --- quaddtype/numpy_quaddtype/src/scalar.c | 10 +++++-- quaddtype/tests/test_quaddtype.py | 36 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/quaddtype/numpy_quaddtype/src/scalar.c b/quaddtype/numpy_quaddtype/src/scalar.c index 8c83f3fb..169372b2 100644 --- a/quaddtype/numpy_quaddtype/src/scalar.c +++ b/quaddtype/numpy_quaddtype/src/scalar.c @@ -15,6 +15,12 @@ #include "scalar_ops.h" #include "dragon4.h" +// For IEEE 754 binary128 (quad precision), we need 36 decimal digits +// to guarantee round-trip conversion (string -> parse -> equals original value) +// Formula: ceil(1 + MANT_DIG * log10(2)) = ceil(1 + 113 * 0.30103) = 36 +// src: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format +#define SLEEF_QUAD_DECIMAL_DIG 36 + QuadPrecisionObject * QuadPrecision_raw_new(QuadBackendType backend) @@ -152,7 +158,7 @@ QuadPrecision_str_dragon4(QuadPrecisionObject *self) Dragon4_Options opt = {.scientific = 0, .digit_mode = DigitMode_Unique, .cutoff_mode = CutoffMode_TotalLength, - .precision = SLEEF_QUAD_DIG, + .precision = SLEEF_QUAD_DECIMAL_DIG, .sign = 1, .trim_mode = TrimMode_LeaveOneZero, .digits_left = 1, @@ -203,7 +209,7 @@ QuadPrecision_repr_dragon4(QuadPrecisionObject *self) Dragon4_Options opt = {.scientific = 1, .digit_mode = DigitMode_Unique, .cutoff_mode = CutoffMode_TotalLength, - .precision = SLEEF_QUAD_DIG, + .precision = SLEEF_QUAD_DECIMAL_DIG, .sign = 1, .trim_mode = TrimMode_LeaveOneZero, .digits_left = 1, diff --git a/quaddtype/tests/test_quaddtype.py b/quaddtype/tests/test_quaddtype.py index 5662a643..5f39bb1c 100644 --- a/quaddtype/tests/test_quaddtype.py +++ b/quaddtype/tests/test_quaddtype.py @@ -12,6 +12,42 @@ def test_create_scalar_simple(): assert isinstance(QuadPrecision(1.63), QuadPrecision) assert isinstance(QuadPrecision(1), QuadPrecision) + +def test_string_roundtrip(): + # Test with various values that require full quad precision + test_values = [ + QuadPrecision("0.417022004702574000667425480060047"), # Random value + QuadPrecision("1.23456789012345678901234567890123456789"), # Many digits + numpy_quaddtype.pi, # Mathematical constant + numpy_quaddtype.e, + QuadPrecision("1e-100"), # Very small + QuadPrecision("1e100"), # Very large + QuadPrecision("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233"), # very precise pi + ] + + for original in test_values: + string_repr = str(original) + reconstructed = QuadPrecision(string_repr) + + # Values should be exactly equal (bit-for-bit identical) + assert reconstructed == original, ( + f"Round-trip failed for {repr(original)}:\n" + f" Original: {repr(original)}\n" + f" String: {string_repr}\n" + f" Reconstructed: {repr(reconstructed)}" + ) + + # Also verify repr() preserves value + repr_str = repr(original) + # Extract the string value from repr format: QuadPrecision('value', backend='...') + value_from_repr = repr_str.split("'")[1] + reconstructed_from_repr = QuadPrecision(value_from_repr) + + assert reconstructed_from_repr == original, ( + f"Round-trip from repr() failed for {repr(original)}" + ) + + @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))]) def test_math_constant(name, expected): assert isinstance(getattr(numpy_quaddtype, name), QuadPrecision)