From 0146a301258e7c9d3a5d16bbb422f4e3f95da246 Mon Sep 17 00:00:00 2001 From: chenyu Date: Sat, 18 Jul 2026 21:58:41 -0400 Subject: [PATCH] improve cast to unsign min_max [pr] (#17078) --- test/null/test_uop_vmin_vmax.py | 7 +++++++ tinygrad/uop/ops.py | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/null/test_uop_vmin_vmax.py b/test/null/test_uop_vmin_vmax.py index 4654fe6ab1..248d002ff1 100644 --- a/test/null/test_uop_vmin_vmax.py +++ b/test/null/test_uop_vmin_vmax.py @@ -127,6 +127,13 @@ class TestVminVmaxProperties(unittest.TestCase): self.assertEqual(x.vmin, 0) self.assertEqual(x.vmax, 10 >> 2) + def test_vmin_vmax_cast_unsigned(self): + # a fitting source keeps exact bounds: no wrap can occur + self.assertEqual(UOp.variable('x', 5, 10).cast(dtypes.uint8)._min_max, (5, 10)) + # a possibly-negative or too-large source can wrap: conservative + self.assertEqual(UOp.variable('x', -1, 10).cast(dtypes.uint8)._min_max, (0, 255)) + self.assertEqual(UOp.variable('x', 250, 260).cast(dtypes.uint8)._min_max, (0, 255)) + def test_vmin_vmax_xor_neg1(self): x = UOp.variable('x', 3, 7) uop = x ^ -1 diff --git a/tinygrad/uop/ops.py b/tinygrad/uop/ops.py index e6b55786d9..ff9992de7e 100644 --- a/tinygrad/uop/ops.py +++ b/tinygrad/uop/ops.py @@ -1029,9 +1029,12 @@ class UOp(RandMixin, metaclass=UOpMetaClass): if self.op is Ops.STACK: return min(x.vmin for x in self.src), max(x.vmax for x in self.src) if self.op is Ops.CONST and self.arg is not Invalid: return self.arg, self.arg if self.op is Ops.INDEX: return self.src[0]._min_max - # TODO: CAST to bool/unsigned is not monotone, still some case can be simplified - if self.op is Ops.CAST and self.dtype in dtypes.floats+dtypes.sints+(dtypes.index,): - return max(self.dtype.min, self.src[0].vmin), min(self.src[0].vmax, self.dtype.max) + if self.op is Ops.CAST: + # a cast to unsigned keeps exact bounds when the source fits + # TODO: can do more based on new dtype window + if dtypes.is_unsigned(self.dtype) and 0 <= self.src[0].vmin and self.src[0].vmax <= self.dtype.max: return self.src[0]._min_max + if self.dtype in dtypes.floats+dtypes.sints+(dtypes.index,): + return max(self.dtype.min, self.src[0].vmin), min(self.src[0].vmax, self.dtype.max) return self.dtype.min, self.dtype.max @functools.cached_property