Skip to content

Commit ff69b8e

Browse files
committed
addded float tests
1 parent 90b824d commit ff69b8e

2 files changed

Lines changed: 74 additions & 80 deletions

File tree

quaddtype/numpy_quaddtype/src/casts.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,12 +1238,18 @@ static inline int quad_to_numpy_same_value_check(quad_value x, QuadBackendType b
12381238
return 1;
12391239
if(Sleef_icmpeqq1(x.sleef_value, roundtrip.sleef_value))
12401240
return 1;
1241+
// Handle -0.0 == +0.0 case: both zeros are considered equal for same_value casting
1242+
if(Sleef_icmpeqq1(x.sleef_value, QUAD_ZERO) && Sleef_icmpeqq1(roundtrip.sleef_value, QUAD_ZERO))
1243+
return 1;
12411244
}
12421245
else {
12431246
if(std::isnan(x.longdouble_value) && std::isnan(roundtrip.longdouble_value))
12441247
return 1;
12451248
if(x.longdouble_value == roundtrip.longdouble_value)
12461249
return 1;
1250+
// Handle -0.0 == +0.0 case for longdouble backend
1251+
if(x.longdouble_value == 0.0L && roundtrip.longdouble_value == 0.0L)
1252+
return 1;
12471253
}
12481254
// Sleef_quad sleef_val = quad_to_sleef_quad(&x, backend);
12491255
Sleef_quad sleef_val = x.sleef_value;

quaddtype/tests/test_quaddtype.py

Lines changed: 68 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5451,86 +5451,74 @@ def test_same_value_cast_quad_to_int(self, dtype, passing, failing):
54515451
with pytest.raises(ValueError):
54525452
q.astype(dtype, casting="same_value")
54535453

5454-
# @pytest.mark.parametrize("dtype,passing,failing", [
5455-
# # float16/half: 11-bit significand (10 explicit + 1 implicit)
5456-
# # max: 65504, exact integers up to 2^11 = 2048
5457-
# ("float16",
5458-
# [0.0, -0.0, float('inf'), float('-inf'), float('nan'),
5459-
# 1.0, -1.0, 0.5, 0.25,
5460-
# 2048.0, # 2^11, largest consecutive integer
5461-
# 65504.0, # max representable
5462-
# 2**-14, # min positive normal
5463-
# ],
5464-
# [65536.0, # overflow (> max)
5465-
# # precision loss (first non-representable int > 2048)
5466-
# 2049.0,
5467-
# 1.0 + 2**-11, # precision loss (step from 1.0 is 2^-10)
5468-
# ]),
5469-
5470-
# ("half", # alias, same values
5471-
# [0.0, float('inf'), float('-inf'), float('nan'), 1.0, 2048.0],
5472-
# [65536.0, 2049.0]),
5473-
5474-
# # float32: 24-bit significand (23 explicit + 1 implicit)
5475-
# # max: ~3.4e38, exact integers up to 2^24 = 16777216
5476-
# ("float32",
5477-
# [0.0, -0.0, float('inf'), float('-inf'), float('nan'),
5478-
# 1.0, -1.0, 0.5, 0.25,
5479-
# 16777216.0, # 2^24, largest consecutive integer
5480-
# 3.4028235e38, # max representable (approx)
5481-
# 2**-126, # min positive normal
5482-
# ],
5483-
# [16777217.0, # precision loss (first non-representable int)
5484-
# 1e39, # overflow
5485-
# 1.0 + 2**-24, # precision loss (step from 1.0 is 2^-23)
5486-
# ]),
5487-
5488-
# ("float", # alias
5489-
# [0.0, float('inf'), float('-inf'), float('nan'), 1.0, 16777216.0],
5490-
# [16777217.0, 1e39]),
5491-
5492-
# # float64: 53-bit significand (52 explicit + 1 implicit)
5493-
# # max: ~1.8e308, exact integers up to 2^53
5494-
# ("float64",
5495-
# [0.0, -0.0, float('inf'), float('-inf'), float('nan'),
5496-
# 1.0, -1.0, 0.5, 0.25,
5497-
# 2.0**53, # largest consecutive integer
5498-
# 1.7976931348623157e308, # max representable (approx)
5499-
# 2**-1022, # min positive normal
5500-
# ],
5501-
# [2.0**53 + 1, # precision loss
5502-
# 1e309, # overflow
5503-
# 1.0 + 2**-53, # precision loss (step from 1.0 is 2^-52)
5504-
# ]),
5505-
5506-
# ("double", # alias
5507-
# [0.0, float('inf'), float('-inf'), float('nan'), 1.0, 2.0**53],
5508-
# [2.0**53 + 1, 1e309]),
5509-
5510-
# # longdouble: platform-dependent!
5511-
# # x86 Linux: 80-bit extended, 64-bit significand → integers up to 2^64
5512-
# # Windows/macOS: often same as float64
5513-
# # Consider using np.finfo(np.longdouble).nmant to adjust dynamically
5514-
# # ("longdouble",
5515-
# # [0.0, -0.0, float('inf'), float('-inf'), float('nan'),
5516-
# # 1.0, 2.0**53, 2.0**53 + 1], # 2^53+1 passes if longdouble > float64
5517-
# # # Failing cases depend on platform - maybe skip or parametrize separately
5518-
# # []),
5519-
# ])
5520-
# def test_same_value_cast_floats(self, dtype, passing, failing):
5521-
# for val in passing:
5522-
# q = np.array([val], dtype=QuadPrecDType())
5523-
# result = q.astype(dtype, casting="same_value")
5524-
# # Use appropriate comparison for nan
5525-
# if np.isnan(val):
5526-
# assert np.isnan(result)
5527-
# else:
5528-
# assert result == val
5529-
5530-
# for val in failing:
5531-
# q = np.array([val], dtype=QuadPrecDType())
5532-
# with pytest.raises(ValueError):
5533-
# q.astype(dtype, casting="same_value")
5454+
@pytest.mark.parametrize("dtype", [
5455+
np.float16, np.float32, np.float64, np.longdouble
5456+
])
5457+
def test_same_value_cast_floats_special_values(self, dtype):
5458+
"""Test that special floating-point values roundtrip correctly."""
5459+
special_values = [0.0, -0.0, float('inf'), float('-inf'), float('nan')]
5460+
5461+
for val in special_values:
5462+
q = np.array([val], dtype=QuadPrecDType())
5463+
result = q.astype(dtype, casting="same_value")
5464+
if np.isnan(val):
5465+
assert np.isnan(result), f"NaN failed for {dtype}"
5466+
else:
5467+
assert result == val, f"{val} failed for {dtype}"
5468+
5469+
@pytest.mark.parametrize("dtype", [
5470+
np.float16, np.float32, np.float64, np.longdouble
5471+
])
5472+
def test_same_value_cast_floats_within_range(self, dtype):
5473+
"""Test values that should roundtrip exactly within dtype's precision."""
5474+
info = np.finfo(dtype)
5475+
5476+
# Values that should pass (exactly representable)
5477+
passing_values = [
5478+
1.0, -1.0, 0.5, -0.5, 0.25, -0.25,
5479+
2.0, 4.0, 8.0, # powers of 2
5480+
info.tiny, # min positive normal
5481+
2 ** info.nmant, # largest consecutive integer
5482+
]
5483+
5484+
for val in passing_values:
5485+
# Ensure the value is representable in the target dtype first
5486+
target_val = dtype(val)
5487+
q = np.array([target_val], dtype=QuadPrecDType())
5488+
result = q.astype(dtype, casting="same_value")
5489+
assert result == target_val, f"Value {val} failed for {dtype}"
5490+
5491+
5492+
@pytest.mark.parametrize("dtype", [
5493+
np.float16, np.float32, np.float64, np.longdouble
5494+
])
5495+
def test_same_value_cast_floats_precision_loss(self, dtype):
5496+
"""Test values that cannot be represented exactly and should fail."""
5497+
from decimal import Decimal, getcontext
5498+
5499+
getcontext().prec = 50 # plenty for quad precision
5500+
info = np.finfo(dtype)
5501+
nmant = info.nmant # 10 for f16, 23 for f32, 52 for f64
5502+
5503+
# First odd integer beyond exact representability
5504+
first_bad_int = 2 ** (nmant + 1) + 1
5505+
# Value between 1.0 and 1.0 + eps (i.e., 1 + eps/2)
5506+
# eps = 2^-nmant, so eps/2 = 2^-(nmant+1)
5507+
one_plus_half_eps = Decimal(1) + Decimal(2) ** -(nmant + 1)
5508+
5509+
# Value between 2.0 and 2.0 + 2*eps
5510+
two_plus_eps = Decimal(2) + Decimal(2) ** -nmant
5511+
5512+
failing_values = [
5513+
str(first_bad_int),
5514+
str(one_plus_half_eps),
5515+
str(two_plus_eps),
5516+
]
5517+
5518+
for val in failing_values:
5519+
q = np.array([val], dtype=QuadPrecDType())
5520+
with pytest.raises(ValueError):
5521+
q.astype(dtype, casting="same_value")
55345522

55355523
# @pytest.mark.parametrize("dtype", [
55365524
# "S50", "U50", "<U50", ">U50", "S100", "U100", "<U100", ">U100", np.dtypes.StringDType()])

0 commit comments

Comments
 (0)