diff --git a/quaddtype/numpy_quaddtype/src/ops.hpp b/quaddtype/numpy_quaddtype/src/ops.hpp index 826cab57..754db054 100644 --- a/quaddtype/numpy_quaddtype/src/ops.hpp +++ b/quaddtype/numpy_quaddtype/src/ops.hpp @@ -243,6 +243,15 @@ quad_atanh(const Sleef_quad *op) return Sleef_atanhq1_u10(*op); } +static inline Sleef_quad +quad_radians(const Sleef_quad *op) +{ + // radians = degrees * π / 180 + static const Sleef_quad one_eighty = sleef_q(+0x1680000000000LL, 0x0000000000000000ULL, 7); + Sleef_quad ratio = Sleef_divq1_u05(SLEEF_M_PIq, one_eighty); + return Sleef_mulq1_u05(*op, ratio); +} + // Unary long double operations typedef long double (*unary_op_longdouble_def)(const long double *); @@ -446,6 +455,16 @@ ld_atanh(const long double *op) return atanhl(*op); } +static inline long double +ld_radians(const long double *op) +{ + // radians = degrees * π / 180 + #ifndef M_PI + #define M_PI 3.14159265358979323846 + #endif + return (*op) * (static_cast(M_PI) / 180.0L); +} + // Unary Quad properties typedef npy_bool (*unary_prop_quad_def)(const Sleef_quad *); diff --git a/quaddtype/numpy_quaddtype/src/umath/unary_ops.cpp b/quaddtype/numpy_quaddtype/src/umath/unary_ops.cpp index a6c69442..944033fc 100644 --- a/quaddtype/numpy_quaddtype/src/umath/unary_ops.cpp +++ b/quaddtype/numpy_quaddtype/src/umath/unary_ops.cpp @@ -250,5 +250,11 @@ init_quad_unary_ops(PyObject *numpy) if (create_quad_unary_ufunc(numpy, "arctanh") < 0) { return -1; } + if (create_quad_unary_ufunc(numpy, "radians") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "deg2rad") < 0) { + return -1; + } return 0; } \ No newline at end of file diff --git a/quaddtype/release_tracker.md b/quaddtype/release_tracker.md index e9b84f37..b2e2250c 100644 --- a/quaddtype/release_tracker.md +++ b/quaddtype/release_tracker.md @@ -55,8 +55,8 @@ | arccosh | ✅ | ✅ | | arctanh | ✅ | ✅ | | degrees | | | -| radians | | | -| deg2rad | | | +| radians | ✅ | ✅ | +| deg2rad | ✅ | ✅ | | rad2deg | | | | bitwise_and | | | | bitwise_or | | | diff --git a/quaddtype/tests/test_quaddtype.py b/quaddtype/tests/test_quaddtype.py index 1a0bc97d..40063cdf 100644 --- a/quaddtype/tests/test_quaddtype.py +++ b/quaddtype/tests/test_quaddtype.py @@ -1905,3 +1905,44 @@ def test_hypot(x1, x2, expected): else: np.testing.assert_allclose(float(result), expected, rtol=1e-13) + +@pytest.mark.parametrize("op", [np.radians, np.deg2rad]) +@pytest.mark.parametrize("degrees,expected_radians", [ + # Basic conversions + (0.0, 0.0), + (30.0, np.pi / 6), + (45.0, np.pi / 4), + (60.0, np.pi / 3), + (90.0, np.pi / 2), + (180.0, np.pi), + (270.0, 3 * np.pi / 2), + (360.0, 2 * np.pi), + # Negative values + (-90.0, -np.pi / 2), + (-180.0, -np.pi), + # Special values + (np.inf, np.inf), + (-np.inf, -np.inf), + (np.nan, np.nan), + # Edge cases + (0.0, 0.0), + (-0.0, -0.0), +]) +def test_radians(op, degrees, expected_radians): + """Test radians and deg2rad ufuncs convert degrees to radians""" + q_deg = QuadPrecision(degrees) + result = op(q_deg) + + assert isinstance(result, QuadPrecision) + + if np.isnan(expected_radians): + assert np.isnan(float(result)) + elif np.isinf(expected_radians): + assert np.isinf(float(result)) + if expected_radians > 0: + assert float(result) > 0 + else: + assert float(result) < 0 + else: + np.testing.assert_allclose(float(result), expected_radians, rtol=1e-13) +