Skip to content

fix(metal): preserve subnormal float values when casting to bool (#4205) - #4224

Open
reckylurker wants to merge 2 commits into
ml-explore:mainfrom
reckylurker:fix/metal-subnormal-bool-cast
Open

fix(metal): preserve subnormal float values when casting to bool (#4205)#4224
reckylurker wants to merge 2 commits into
ml-explore:mainfrom
reckylurker:fix/metal-subnormal-bool-cast

Conversation

@reckylurker

@reckylurker reckylurker commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #4205

Proposed changes

In Metal Shading Language (MSL), casting a floating-point number directly to bool via static_cast<bool>(x) evaluates in float registers, which flushes subnormal numbers to zero in hardware. So static_cast<bool>(subnormal) ends up evaluating as 0.0f != 0.0f -> false.

This was affecting .astype(mx.bool_) (in copy.h) as well as any(), all(), and other reductions whenever U = bool.

To fix this, introduced a mlx_cast<U, T> helper in utils.h that checks the raw bit pattern for floats (ignoring the sign bit, e.g. (as_type<uint32_t>(val) & 0x7FFFFFFF) != 0). This checks the exponent and fraction bits as raw integers so subnormals don't get flushed by Metal hardware units, while still keeping -0.0 as False.

updated copy.h, reduce_all.h, reduce_col.h, and reduce_row.h to use mlx_cast<U>.

Added test_subnormal_bool_cast in python/tests/test_ops.py to check astype(bool), any(), and all() on float32 and bfloat16 subnormals. All Python and C++ tests pass!

Checklist

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the necessary documentation (if needed)

@zcbenz zcbenz added await verification This pull request is non-trivial and requires a human expert to verify its correctness. and removed await verification This pull request is non-trivial and requires a human expert to verify its correctness. labels Aug 13, 2026
Comment thread mlx/backend/metal/kernels/utils.h Outdated
///////////////////////////////////////////////////////////////////////////////

template <typename U, typename T>
inline U mlx_cast(T val) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cuda backend there is a similar utility named cast_to, I think we can use the same name here.

Comment thread mlx/backend/metal/kernels/utils.h Outdated

template <>
inline bool mlx_cast<bool, half>(half val) {
return (as_type<uint16_t>(val) & 0x7FFF) != 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float16 does not seem to need this, should add a test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, looks like half values are not affected in MSL. Thanks for the suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] any/all and astype(bool) flush float subnormals to zero on Metal

2 participants