@@ -2065,3 +2065,194 @@ def test_radians(op, degrees, expected_radians):
20652065 else :
20662066 np .testing .assert_allclose (float (result ), expected_radians , rtol = 1e-13 )
20672067
2068+
2069+ class TestNextAfter :
2070+ """Test cases for np.nextafter function with QuadPrecision dtype"""
2071+
2072+ @pytest .mark .parametrize ("x1,x2" , [
2073+ # NaN tests
2074+ (np .nan , 1.0 ),
2075+ (1.0 , np .nan ),
2076+ (np .nan , np .nan ),
2077+ ])
2078+ def test_nan (self , x1 , x2 ):
2079+ """Test nextafter with NaN inputs returns NaN"""
2080+ q_x1 = QuadPrecision (x1 )
2081+ q_x2 = QuadPrecision (x2 )
2082+
2083+ result = np .nextafter (q_x1 , q_x2 )
2084+
2085+ assert isinstance (result , QuadPrecision )
2086+ assert np .isnan (float (result ))
2087+
2088+ def test_precision (self ):
2089+ """Test that nextafter provides the exact next representable value"""
2090+ # Start with 1.0 and move towards 2.0
2091+ x1 = QuadPrecision (1.0 )
2092+ x2 = QuadPrecision (2.0 )
2093+
2094+ result = np .nextafter (x1 , x2 )
2095+
2096+ # Get machine epsilon from finfo
2097+ finfo = np .finfo (QuadPrecDType ())
2098+ expected = x1 + finfo .eps
2099+
2100+ # result should be exactly 1.0 + eps
2101+ assert result == expected
2102+
2103+ # Moving the other direction should give us back 1.0
2104+ result_back = np .nextafter (result , x1 )
2105+ assert result_back == x1
2106+
2107+ def test_smallest_subnormal (self ):
2108+ """Test that nextafter(0.0, 1.0) returns the smallest positive subnormal (TRUE_MIN)"""
2109+ zero = QuadPrecision (0.0 )
2110+ one = QuadPrecision (1.0 )
2111+
2112+ result = np .nextafter (zero , one ) # smallest_subnormal
2113+ finfo = np .finfo (QuadPrecDType ())
2114+
2115+ assert result == finfo .smallest_subnormal , \
2116+ f"nextafter(0.0, 1.0) should equal smallest_subnormal, got { result } vs { finfo .smallest_subnormal } "
2117+
2118+ # Verify it's positive and very small
2119+ assert result > zero , "nextafter(0.0, 1.0) should be positive"
2120+
2121+ # Moving back towards zero should give us zero
2122+ result_back = np .nextafter (result , zero )
2123+ assert result_back == zero , f"nextafter(smallest_subnormal, 0.0) should be 0.0, got { result_back } "
2124+
2125+ def test_negative_zero (self ):
2126+ """Test nextafter with negative zero"""
2127+ neg_zero = QuadPrecision (- 0.0 )
2128+ pos_zero = QuadPrecision (0.0 )
2129+ one = QuadPrecision (1.0 )
2130+ neg_one = QuadPrecision (- 1.0 )
2131+
2132+ finfo = np .finfo (QuadPrecDType ())
2133+
2134+ # nextafter(-0.0, 1.0) should return smallest positive subnormal
2135+ result = np .nextafter (neg_zero , one )
2136+ assert result == finfo .smallest_subnormal , \
2137+ f"nextafter(-0.0, 1.0) should be smallest_subnormal, got { result } "
2138+ assert result > pos_zero , "Result should be positive"
2139+
2140+ # nextafter(+0.0, -1.0) should return smallest negative subnormal
2141+ result_neg = np .nextafter (pos_zero , neg_one )
2142+ expected_neg_subnormal = - finfo .smallest_subnormal
2143+ assert result_neg == expected_neg_subnormal , \
2144+ f"nextafter(+0.0, -1.0) should be -smallest_subnormal, got { result_neg } "
2145+ assert result_neg < pos_zero , "Result should be negative"
2146+
2147+ def test_infinity_cases (self ):
2148+ """Test nextafter with infinity edge cases"""
2149+ pos_inf = QuadPrecision (np .inf )
2150+ neg_inf = QuadPrecision (- np .inf )
2151+ one = QuadPrecision (1.0 )
2152+ neg_one = QuadPrecision (- 1.0 )
2153+ zero = QuadPrecision (0.0 )
2154+
2155+ finfo = np .finfo (QuadPrecDType ())
2156+
2157+ # nextafter(+inf, finite) should return max finite value
2158+ result = np .nextafter (pos_inf , zero )
2159+ assert not np .isinf (result ), "nextafter(+inf, 0) should be finite"
2160+ assert result < pos_inf , "Result should be less than +inf"
2161+ assert result == finfo .max , f"nextafter(+inf, 0) should be max, got { result } vs { finfo .max } "
2162+
2163+ # nextafter(-inf, finite) should return -max (most negative finite)
2164+ result_neg = np .nextafter (neg_inf , zero )
2165+ assert not np .isinf (result_neg ), "nextafter(-inf, 0) should be finite"
2166+ assert result_neg > neg_inf , "Result should be greater than -inf"
2167+ assert result_neg == - finfo .max , f"nextafter(-inf, 0) should be -max, got { result_neg } "
2168+
2169+ # Verify symmetry: nextafter(result, +inf) should give us +inf back
2170+ back_to_inf = np .nextafter (result , pos_inf )
2171+ assert back_to_inf == pos_inf , "nextafter(max_finite, +inf) should be +inf"
2172+
2173+ # nextafter(+inf, +inf) should return +inf
2174+ result_inf = np .nextafter (pos_inf , pos_inf )
2175+ assert result_inf == pos_inf , "nextafter(+inf, +inf) should be +inf"
2176+
2177+ # nextafter(-inf, -inf) should return -inf
2178+ result_neg_inf = np .nextafter (neg_inf , neg_inf )
2179+ assert result_neg_inf == neg_inf , "nextafter(-inf, -inf) should be -inf"
2180+
2181+ def test_max_to_infinity (self ):
2182+ """Test nextafter from max finite value to infinity"""
2183+ finfo = np .finfo (QuadPrecDType ())
2184+ max_val = finfo .max
2185+ pos_inf = QuadPrecision (np .inf )
2186+ neg_inf = QuadPrecision (- np .inf )
2187+
2188+ # nextafter(max_finite, +inf) should return +inf
2189+ result = np .nextafter (max_val , pos_inf )
2190+ assert np .isinf (result ), f"nextafter(max, +inf) should be inf, got { result } "
2191+ assert result > max_val , "Result should be greater than max"
2192+ assert result == pos_inf , "Result should be +inf"
2193+
2194+ # nextafter(-max_finite, -inf) should return -inf
2195+ neg_max_val = - max_val
2196+ result_neg = np .nextafter (neg_max_val , neg_inf )
2197+ assert np .isinf (result_neg ), f"nextafter(-max, -inf) should be -inf, got { result_neg } "
2198+ assert result_neg < neg_max_val , "Result should be less than -max"
2199+ assert result_neg == neg_inf , "Result should be -inf"
2200+
2201+ def test_near_max (self ):
2202+ """Test nextafter near maximum finite value"""
2203+ finfo = np .finfo (QuadPrecDType ())
2204+ max_val = finfo .max
2205+ zero = QuadPrecision (0.0 )
2206+ pos_inf = QuadPrecision (np .inf )
2207+
2208+ # nextafter(max, 0) should return a value less than max
2209+ result = np .nextafter (max_val , zero )
2210+ assert result < max_val , "nextafter(max, 0) should be less than max"
2211+ assert not np .isinf (result ), "Result should be finite"
2212+
2213+ # The difference should be one ULP at that scale
2214+ # Moving back should give us max again
2215+ result_back = np .nextafter (result , pos_inf )
2216+ assert result_back == max_val , f"Moving back should return max, got { result_back } "
2217+
2218+ def test_symmetry (self ):
2219+ """Test symmetry properties of nextafter"""
2220+ values = [0.0 , 1.0 , - 1.0 , 1e10 , - 1e10 , 1e-10 , - 1e-10 ]
2221+
2222+ for val in values :
2223+ q_val = QuadPrecision (val )
2224+
2225+ # nextafter(x, +direction) then nextafter(result, x) should return x
2226+ if not np .isinf (val ):
2227+ result_up = np .nextafter (q_val , QuadPrecision (np .inf ))
2228+ result_back = np .nextafter (result_up , q_val )
2229+ assert result_back == q_val , \
2230+ f"Symmetry failed for { val } : nextafter then back should return original"
2231+
2232+ # Same for down direction
2233+ result_down = np .nextafter (q_val , QuadPrecision (- np .inf ))
2234+ result_back_down = np .nextafter (result_down , q_val )
2235+ assert result_back_down == q_val , \
2236+ f"Symmetry failed for { val } : nextafter down then back should return original"
2237+
2238+ def test_direction (self ):
2239+ """Test that nextafter moves in the correct direction"""
2240+ test_cases = [
2241+ (1.0 , 2.0 , "greater" ), # towards larger
2242+ (2.0 , 1.0 , "less" ), # towards smaller
2243+ (- 1.0 , - 2.0 , "less" ), # towards more negative
2244+ (- 2.0 , - 1.0 , "greater" ), # towards less negative
2245+ (1.0 , np .inf , "greater" ), # towards +inf
2246+ (1.0 , - np .inf , "less" ), # towards -inf
2247+ ]
2248+
2249+ for x , y , expected_dir in test_cases :
2250+ q_x = QuadPrecision (x )
2251+ q_y = QuadPrecision (y )
2252+ result = np .nextafter (q_x , q_y )
2253+
2254+ if expected_dir == "greater" :
2255+ assert result > q_x , f"nextafter({ x } , { y } ) should be > { x } , got { result } "
2256+ elif expected_dir == "less" :
2257+ assert result < q_x , f"nextafter({ x } , { y } ) should be < { x } , got { result } "
2258+
0 commit comments