diff --git a/test/backend/test_dtype_alu.py b/test/backend/test_dtype_alu.py index 0e4b60faeb..6a182616c8 100644 --- a/test/backend/test_dtype_alu.py +++ b/test/backend/test_dtype_alu.py @@ -399,9 +399,10 @@ class TestDTypeALU(unittest.TestCase): if float_dtype not in supported_dtypes: float_dtype = dtypes.float32 universal_test_cast(a, float_dtype, unsigned_dtype) - @unittest.expectedFailure - def test_unsafe_cast_float_to_int_failure(self): - val = float(dtypes.int32.max - 1) + def test_unsafe_cast_float_to_int(self): + # the value is off the float32 grid but rounds in-range: the buffer and const-fold paths must agree + # (out-of-range float->int cast stays undefined: hardware may saturate where the fold wraps) + val = 2147483000.0 t1 = Tensor([val], dtype=dtypes.float32).cast(dtypes.int32) t2 = Tensor(val, dtype=dtypes.float32).cast(dtypes.int32) np.testing.assert_equal(t1.item(), t2.item()) diff --git a/test/null/test_const_folding.py b/test/null/test_const_folding.py index 0f217e8fa5..5e365c4233 100644 --- a/test/null/test_const_folding.py +++ b/test/null/test_const_folding.py @@ -1,6 +1,6 @@ import unittest, itertools, math from tinygrad import Tensor, dtypes, Context -from tinygrad.dtype import DType, ConstType +from tinygrad.dtype import DType, ConstType, truncate from tinygrad.uop.ops import Ops, UOp from test.helpers import full_rewrite import numpy as np @@ -51,6 +51,17 @@ class TestWeakConstFolding(unittest.TestCase): def test_invalid_poison(self): self.assertTrue(UOp.invalid().alu(Ops.CDIV, UOp.const(0)).simplify().is_invalid) + def test_cast_commits_to_dtype_grid(self): + # committing a weak const to a stated width puts the value on that width's grid, same as storage packing and native compilers + v = 1/123008 # not representable in float16 + out = UOp.const(v).cast(dtypes.half).simplify() + self.assertEqual((out.op, out.dtype, out.val), (Ops.CONST, dtypes.half, truncate[dtypes.half](v))) + self.assertNotEqual(out.val, v) + # the grid commit preserves the sign of zero + self.assertEqual(math.copysign(1, UOp.const(-0.0).cast(dtypes.half).simplify().val), -1) + # observable at tensor level: the const-folded comparison agrees with the committed value + self.assertTrue((Tensor(-3.2).cast(dtypes.float32) <= truncate[dtypes.float32](-3.2)).item()) + class TestBinaryOpsConstFolding(unittest.TestCase): def test_add_literal_zero(self): _check_ast_count(0, Tensor([1.0, 2, 3, 4]) + 0) diff --git a/test/null/test_uop_vmin_vmax.py b/test/null/test_uop_vmin_vmax.py index fd5c3a7ab2..c42110b2ec 100644 --- a/test/null/test_uop_vmin_vmax.py +++ b/test/null/test_uop_vmin_vmax.py @@ -1,6 +1,6 @@ import unittest, math from tinygrad.uop.ops import UOp, Ops -from tinygrad.dtype import dtypes, Invalid +from tinygrad.dtype import dtypes, Invalid, truncate class TestVminVmaxProperties(unittest.TestCase): def test_vmin_vmax_constant(self): @@ -317,8 +317,8 @@ class TestVminVmaxVConst(unittest.TestCase): def test_vmin_vmax_vconst_with_floats(self): # vmin and vmax for a vector constant of float values uop = UOp.const((1.5, -3.2, 0.0)) - self.assertEqual(uop.vmin, -3.2) - self.assertEqual(uop.vmax, 1.5) + self.assertEqual(uop.vmin, truncate[dtypes.default_float](-3.2)) + self.assertEqual(uop.vmax, truncate[dtypes.default_float](1.5)) def test_vmin_vmax_vconst_with_bools(self): # vmin and vmax for a vector constant of bool values diff --git a/tinygrad/dtype.py b/tinygrad/dtype.py index 62464e02ea..9c0b336a58 100644 --- a/tinygrad/dtype.py +++ b/tinygrad/dtype.py @@ -80,7 +80,7 @@ class DType(metaclass=DTypeMetaClass): # NOTE: float('nan') != float('nan'), so we canonicalize here if isinstance(val, float) and math.isnan(val): val = math.nan # int is the default. wrap floats in ConstFloat to distinguish -0.0 from 0.0 in cache - return ConstFloat(float(val)) if dtypes.is_float(self) else bool(val) if dtypes.is_bool(self) else int(val) + return ConstFloat(truncate.get(self, float)(float(val))) if dtypes.is_float(self) else bool(val) if dtypes.is_bool(self) else int(val) class DTypes: