@@ -2731,6 +2731,70 @@ def test_mod(a, b, backend, op):
27312731 assert result_negative == numpy_negative , f"Sign mismatch for { a } % { b } : quad={ result_negative } , numpy={ numpy_negative } "
27322732
27332733
2734+ @pytest .mark .parametrize ("op" , [np .mod , np .remainder ])
2735+ @pytest .mark .parametrize ("a_func,a_arg,b_str" , [
2736+ # Regression: huge dividend, finite divisor. cosh(-11357.2...) is
2737+ # ~1.19e+4932, so |a/b| is enormous. The old `a - floor(a/b)*b` formula
2738+ # lost all precision here because 0.5 ulp of the quotient corresponds
2739+ # to a huge absolute error. The fmod-based path avoids this.
2740+ ("cosh" , "-11357.216553474703894801348310092223" ,
2741+ "-1.7976931345860068e+308" ),
2742+ # Same with mismatched signs — exercises the Python-convention sign
2743+ # adjustment on a huge result.
2744+ ("cosh" , "-11357.216553474703894801348310092223" ,
2745+ "1.7976931345860068e+308" ),
2746+ # Large quotient, small divisor.
2747+ ("exp" , "1000" , "3.14159265358979323846264338327950288" ),
2748+ ])
2749+ def test_mod_high_precision_sleef (a_func , a_arg , b_str , op ):
2750+ """Sleef backend: verified against mpmath at binary128 precision.
2751+
2752+ These inputs lie far outside f64 range, so the standard test_mod path
2753+ cannot exercise them — mpmath is the only viable ground truth.
2754+ """
2755+ mp .prec = 113
2756+ mp_a = getattr (mp , a_func )(mp .mpf (a_arg ))
2757+ mp_b = mp .mpf (b_str )
2758+ # mp.fmod is Python-style (sign of divisor), matching np.mod.
2759+ mp_result = mp .fmod (mp_a , mp_b )
2760+ expected = QuadPrecision (mp .nstr (mp_result , 36 ))
2761+
2762+ quad_a = getattr (np , a_func )(QuadPrecision (a_arg ))
2763+ quad_b = QuadPrecision (b_str )
2764+ quad_result = op (quad_a , quad_b )
2765+
2766+ assert np .isclose (quad_result , expected , rtol = 1e-34 , atol = 0 ), (
2767+ f"sleef mismatch for { op .__name__ } ({ a_func } ({ a_arg } ), { b_str } ): "
2768+ f"quad={ quad_result } , expected={ expected } "
2769+ )
2770+
2771+
2772+ @pytest .mark .parametrize ("op" , [np .mod , np .remainder ])
2773+ @pytest .mark .parametrize ("a_func,a_arg,b_str" , [
2774+ ("exp" , "700" , "3.14159265358979323846264338327950288" ),
2775+ ])
2776+ def test_mod_high_precision_longdouble (a_func , a_arg , b_str , op ):
2777+ """Longdouble backend: cross-checked against numpy's np.longdouble.
2778+
2779+ The backend stores a C `long double`, the same type as np.longdouble,
2780+ so np.mod on np.longdouble inputs is a direct same-precision reference
2781+ and the result should match bit-for-bit.
2782+ """
2783+ np_a = getattr (np , a_func )(np .longdouble (a_arg ))
2784+ np_b = np .longdouble (b_str )
2785+ expected = op (np_a , np_b )
2786+
2787+ quad_a = getattr (np , a_func )(QuadPrecision (a_arg , backend = "longdouble" ))
2788+ quad_b = QuadPrecision (b_str , backend = "longdouble" )
2789+ quad_result = op (quad_a , quad_b )
2790+
2791+ # np.longdouble(QuadPrecision) is a lossless cast (same underlying type).
2792+ assert np .longdouble (quad_result ) == expected , (
2793+ f"longdouble mismatch for { op .__name__ } ({ a_func } ({ a_arg } ), { b_str } ): "
2794+ f"quad={ np .longdouble (quad_result )!r} , expected={ expected !r} "
2795+ )
2796+
2797+
27342798@pytest .mark .parametrize ("backend" , ["sleef" , "longdouble" ])
27352799@pytest .mark .parametrize ("a,b" , [
27362800 # Basic cases - positive/positive
0 commit comments