From 9ce96c26284cebe57ccab70e9b9b02fb0e3282cb Mon Sep 17 00:00:00 2001 From: chenyu Date: Mon, 13 Jul 2026 18:53:13 -0400 Subject: [PATCH] fix subnormal in test_dtype (#17013) * fix subnormal in test_dtype should fix flaky test/backend/test_dtype.py::TestFp8e4m3::test_casts_from * better --- test/backend/test_dtype.py | 5 ++++- test/helpers.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/backend/test_dtype.py b/test/backend/test_dtype.py index f2bd2a1fcc..a5e73449cd 100644 --- a/test/backend/test_dtype.py +++ b/test/backend/test_dtype.py @@ -8,7 +8,7 @@ from tinygrad.renderer.ptx import PTXRenderer from tinygrad.renderer.nir import NIRRenderer from tinygrad import Context, Device, Tensor, dtypes from hypothesis import given, settings, strategies as strat -from test.helpers import rand_for_dtype +from test.helpers import rand_for_dtype, min_normal from test.unit.test_dtype_spec import _assert_eq, core_dtypes, dtype_ints, dtype_floats, FP8E4M3_MAX, FP8E5M2_MAX, FP8E4M3FNUZ_MAX, FP8E5M2FNUZ_MAX import pytest pytestmark = pytest.mark.filterwarnings("ignore") @@ -46,6 +46,9 @@ def _test_cast(a:Tensor, target_dtype:DType): if a.is_floating_point() and dtypes.is_unsigned(target_dtype): # converting negative float to unsigned integer is undefined a = a.abs() + if a.is_floating_point() and dtypes.is_float(target_dtype) and (mn:=min_normal(target_dtype)) >= min_normal(a.dtype): + # subnormals are zero, so an input below the target's min normal casts to 0 + a = (a.abs() < mn).where(0, a) expected = list(a.numpy().astype(_to_np_dtype(target_dtype))) if target_dtype in dtypes.fp8s: expected = [truncate[target_dtype](x) for x in expected] diff --git a/test/helpers.py b/test/helpers.py index 29fe29a6ff..04e28f1900 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -63,6 +63,8 @@ def assert_jit_cache_len(fxn, expected_len): else: assert len(linear.src) == expected_len, f"expected {expected_len}, got {len(linear.src)}" +def min_normal(dt:DType) -> float: return 2.0 ** (2 - (1 << (dtypes.finfo(dt)[0] - 1))) + def rand_for_dtype(dt:DType, size:int, allow_subnormal=True): if dtypes.is_unsigned(dt): return np.random.randint(0, 100, size=size, dtype=_to_np_dtype(dt)) @@ -71,9 +73,7 @@ def rand_for_dtype(dt:DType, size:int, allow_subnormal=True): elif dt == dtypes.bool: return np.random.choice([True, False], size=size) ret = np.random.uniform(-10, 10, size=size).astype(_to_np_dtype(dt)) - if not allow_subnormal: - min_normal = 2.0 ** (2 - (1 << (dtypes.finfo(dt)[0] - 1))) - ret = np.where(np.abs(ret) < min_normal, 0, ret) + if not allow_subnormal: ret = np.where(np.abs(ret) < min_normal(dt), 0, ret) return ret def timeit(fxn:Callable[..., T], *args, **kwargs) -> tuple[T, float]: