@@ -35,22 +35,28 @@ def test_basic_equality():
35
35
36
36
37
37
@pytest .mark .parametrize ("op" , ["add" , "sub" , "mul" , "truediv" , "pow" , "copysign" ])
38
- @pytest .mark .parametrize ("other" , ["3.0" , "12.5" , "100.0" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
39
- def test_binary_ops (op , other ):
40
- if op == "truediv" and float (other ) == 0 :
38
+ @pytest .mark .parametrize ("a" , ["3.0" , "12.5" , "100.0" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
39
+ @pytest .mark .parametrize ("b" , ["3.0" , "12.5" , "100.0" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
40
+ def test_binary_ops (op , a , b ):
41
+ if op == "truediv" and float (b ) == 0 :
41
42
pytest .xfail ("float division by zero" )
42
43
43
44
op_func = getattr (operator , op , None ) or getattr (np , op )
44
- quad_a = QuadPrecision ("12.5" )
45
- quad_b = QuadPrecision (other )
46
- float_a = 12.5
47
- float_b = float (other )
45
+ quad_a = QuadPrecision (a )
46
+ quad_b = QuadPrecision (b )
47
+ float_a = float ( a )
48
+ float_b = float (b )
48
49
49
50
quad_result = op_func (quad_a , quad_b )
50
51
float_result = op_func (float_a , float_b )
51
52
52
53
np .testing .assert_allclose (np .float64 (quad_result ), float_result , atol = 1e-10 , rtol = 0 , equal_nan = True )
53
54
55
+ # Check sign for zero results
56
+ if float_result == 0.0 :
57
+ assert np .signbit (float_result ) == np .signbit (
58
+ quad_result ), f"Zero sign mismatch for { op } ({ a } , { b } )"
59
+
54
60
55
61
@pytest .mark .parametrize ("op" , ["eq" , "ne" , "le" , "lt" , "ge" , "gt" ])
56
62
@pytest .mark .parametrize ("a" , ["3.0" , "12.5" , "100.0" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
@@ -91,8 +97,20 @@ def test_array_minmax(op, a, b):
91
97
quad_res = op_func (quad_a , quad_b )
92
98
float_res = op_func (float_a , float_b )
93
99
100
+ # native implementation may not be sensitive to zero signs
101
+ # but we want to enforce it for the quad dtype
102
+ # e.g. min(+0.0, -0.0) = -0.0
103
+ if float_a == 0.0 and float_b == 0.0 :
104
+ assert float_res == 0.0
105
+ float_res = np .copysign (0.0 , op_func (np .copysign (1.0 , float_a ), np .copysign (1.0 , float_b )))
106
+
94
107
np .testing .assert_array_equal (quad_res .astype (float ), float_res )
95
108
109
+ # Check sign for zero results
110
+ if float_res == 0.0 :
111
+ assert np .signbit (float_res ) == np .signbit (
112
+ quad_res ), f"Zero sign mismatch for { op } ({ a } , { b } )"
113
+
96
114
97
115
@pytest .mark .parametrize ("op" , ["amin" , "amax" , "nanmin" , "nanmax" ])
98
116
@pytest .mark .parametrize ("a" , ["3.0" , "12.5" , "100.0" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
@@ -105,8 +123,20 @@ def test_array_aminmax(op, a, b):
105
123
quad_res = op_func (quad_ab )
106
124
float_res = op_func (float_ab )
107
125
126
+ # native implementation may not be sensitive to zero signs
127
+ # but we want to enforce it for the quad dtype
128
+ # e.g. min(+0.0, -0.0) = -0.0
129
+ if float (a ) == 0.0 and float (b ) == 0.0 :
130
+ assert float_res == 0.0
131
+ float_res = np .copysign (0.0 , op_func (np .array ([np .copysign (1.0 , float (a )), np .copysign (1.0 , float (b ))])))
132
+
108
133
np .testing .assert_array_equal (np .array (quad_res ).astype (float ), float_res )
109
134
135
+ # Check sign for zero results
136
+ if float_res == 0.0 :
137
+ assert np .signbit (float_res ) == np .signbit (
138
+ quad_res ), f"Zero sign mismatch for { op } ({ a } , { b } )"
139
+
110
140
111
141
@pytest .mark .parametrize ("op" , ["negative" , "positive" , "absolute" , "sign" , "signbit" , "isfinite" , "isinf" , "isnan" , "sqrt" , "square" , "reciprocal" ])
112
142
@pytest .mark .parametrize ("val" , ["3.0" , "-3.0" , "12.5" , "100.0" , "1e100" , "0.0" , "-0.0" , "inf" , "-inf" , "nan" , "-nan" ])
@@ -126,7 +156,7 @@ def test_unary_ops(op, val):
126
156
127
157
np .testing .assert_array_equal (np .array (quad_result ).astype (float ), float_result )
128
158
129
- if op in ["negative " , "positive " , "absolute " , "sign" ] :
159
+ if ( float_result == 0.0 ) and ( op not in ["signbit " , "isfinite " , "isinf " , "isnan" ]) :
130
160
assert np .signbit (float_result ) == np .signbit (quad_result )
131
161
132
162
@@ -290,6 +320,11 @@ def test_logarithmic_functions(op, val):
290
320
np .testing .assert_allclose (float (quad_result ), float_result , rtol = rtol , atol = atol ,
291
321
err_msg = f"Value mismatch for { op } ({ val } )" )
292
322
323
+ # Check sign for zero results
324
+ if float_result == 0.0 :
325
+ assert np .signbit (float_result ) == np .signbit (
326
+ quad_result ), f"Zero sign mismatch for { op } ({ a } , { b } )"
327
+
293
328
294
329
@pytest .mark .parametrize ("val" , [
295
330
# Basic cases around -1 (critical point for log1p)
@@ -304,6 +339,8 @@ def test_logarithmic_functions(op, val):
304
339
"-1.1" , "-2.0" , "-10.0" ,
305
340
# Large positive values
306
341
"1e10" , "1e15" , "1e100" ,
342
+ # Edge cases
343
+ "0.0" , "-0.0" ,
307
344
# Special values
308
345
"inf" , "-inf" , "nan" , "-nan"
309
346
])
@@ -341,9 +378,16 @@ def test_log1p(val):
341
378
np .testing .assert_allclose (float (quad_result ), float_result , rtol = rtol , atol = atol ,
342
379
err_msg = f"Value mismatch for log1p({ val } )" )
343
380
381
+ # Check sign for zero results
382
+ if float_result == 0.0 :
383
+ assert np .signbit (float_result ) == np .signbit (
384
+ quad_result ), f"Zero sign mismatch for { op } ({ val } )"
385
+
344
386
def test_inf ():
345
387
assert QuadPrecision ("inf" ) > QuadPrecision ("1e1000" )
388
+ assert np .signbit (QuadPrecision ("inf" )) == 0
346
389
assert QuadPrecision ("-inf" ) < QuadPrecision ("-1e1000" )
390
+ assert np .signbit (QuadPrecision ("-inf" )) == 1
347
391
348
392
349
393
def test_dtype_creation ():
@@ -448,3 +492,58 @@ def test_mod(a, b, backend, op):
448
492
numpy_negative = numpy_result < 0
449
493
450
494
assert result_negative == numpy_negative , f"Sign mismatch for { a } % { b } : quad={ result_negative } , numpy={ numpy_negative } "
495
+
496
+
497
+ @pytest .mark .parametrize ("op" , ["sinh" , "cosh" , "tanh" , "arcsinh" , "arccosh" , "arctanh" ])
498
+ @pytest .mark .parametrize ("val" , [
499
+ # Basic cases
500
+ "0.0" , "-0.0" , "1.0" , "-1.0" , "2.0" , "-2.0" ,
501
+ # Small values
502
+ "1e-10" , "-1e-10" , "1e-15" , "-1e-15" ,
503
+ # Values near one
504
+ "0.9" , "-0.9" , "0.9999" , "-0.9999" ,
505
+ "1.1" , "-1.1" , "1.0001" , "-1.0001" ,
506
+ # Medium values
507
+ "10.0" , "-10.0" , "20.0" , "-20.0" ,
508
+ # Large values
509
+ "100.0" , "200.0" , "700.0" , "1000.0" , "1e100" , "1e308" ,
510
+ "-100.0" , "-200.0" , "-700.0" , "-1000.0" , "-1e100" , "-1e308" ,
511
+ # Fractional values
512
+ "0.5" , "-0.5" , "1.5" , "-1.5" , "2.5" , "-2.5" ,
513
+ # Special values
514
+ "inf" , "-inf" , "nan" , "-nan"
515
+ ])
516
+ def test_hyperbolic_functions (op , val ):
517
+ """Comprehensive test for hyperbolic functions: sinh, cosh, tanh, arcsinh, arccosh, arctanh"""
518
+ op_func = getattr (np , op )
519
+
520
+ quad_val = QuadPrecision (val )
521
+ float_val = float (val )
522
+
523
+ quad_result = op_func (quad_val )
524
+ float_result = op_func (float_val )
525
+
526
+ # Handle NaN cases
527
+ if np .isnan (float_result ):
528
+ assert np .isnan (
529
+ float (quad_result )), f"Expected NaN for { op } ({ val } ), got { float (quad_result )} "
530
+ return
531
+
532
+ # Handle infinity cases
533
+ if np .isinf (float_result ):
534
+ assert np .isinf (
535
+ float (quad_result )), f"Expected inf for { op } ({ val } ), got { float (quad_result )} "
536
+ assert np .sign (float_result ) == np .sign (
537
+ float (quad_result )), f"Infinity sign mismatch for { op } ({ val } )"
538
+ return
539
+
540
+ # For finite non-zero results
541
+ # Use relative tolerance for exponential functions due to their rapid growth
542
+ rtol = 1e-13 if abs (float_result ) < 1e100 else 1e-10
543
+ np .testing .assert_allclose (float (quad_result ), float_result , rtol = rtol , atol = 1e-15 ,
544
+ err_msg = f"Value mismatch for { op } ({ val } )" )
545
+
546
+ # Check sign for zero results
547
+ if float_result == 0.0 :
548
+ assert np .signbit (float_result ) == np .signbit (
549
+ quad_result ), f"Zero sign mismatch for { op } ({ val } )"
0 commit comments