Skip to content

[MPS] Fix torch.nextafter for bfloat16 - #190481

Closed
sahil-mangla wants to merge 2 commits into
pytorch:mainfrom
sahil-mangla:fix-mps-bfloat16-nextafter
Closed

sahil-mangla wants to merge 2 commits into
pytorch:mainfrom
sahil-mangla:fix-mps-bfloat16-nextafter

Conversation

@sahil-mangla

@sahil-mangla sahil-mangla commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

On MPS, torch.nextafter was returning its input unchanged for bfloat16, because nextafter_functor in BinaryKernel.metal called ::metal::nextafter, which does not have a bfloat overload.
This caused the arguments to be promoted to float, stepped by one fp32 ULP, and then cast back to bfloat, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for bfloat inside nextafter_functor in BinaryKernel.metal using its ushort representation, closely mirroring the CPU implementation in c10/util/BFloat16-math.h and extend nextafter unit test to cover bfloat as well

Fixes #190476

On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`. The root cause was that `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload. This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

We fix this by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, similar to the CPU implementation in `c10/util/BFloat16-math.h`.

We also fix a build issue in `scripts/write_metallib_headers.py` where output directories were not created before trying to write metallib wrapper headers, which caused file not found errors during local compilation.

This PR was authored with the assistance of an AI assistant.

Test Plan:
Verify the fix by running:
```bash
python -c "import torch; a=torch.tensor(1.0, dtype=torch.bfloat16, device='mps'); b=torch.tensor(2.0, dtype=torch.bfloat16, device='mps'); print(torch.nextafter(a, b).cpu())"
```
And verify it matches the CPU output:
```bash
python -c "import torch; a=torch.tensor(1.0, dtype=torch.bfloat16); b=torch.tensor(2.0, dtype=torch.bfloat16); print(torch.nextafter(a, b))"
```
@sahil-mangla
sahil-mangla requested a review from malfet as a code owner July 19, 2026 08:38
@pytorch-bot

pytorch-bot Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/190481

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure

As of commit 7da0d06 with merge base b838c64 (image):

NEW FAILURE - The following job has failed:

  • Mac MPS / macos-py3-arm64-mps / test (test_mps, 1, 1, macos-m2-15) (gh)
    Process completed with exit code 139.

    AI verdict: AI verdict: probably not related

    The segfault occurs in test_metal_capture, which compiles and captures a completely separate custom Metal shader ("kernel void full(...)") unrelated to the nextafter changes in BinaryKernel.metal. All regular MPS tests (both shards) passed successfully, including tests that would exercise the modified shader code.

    Full reasoning on HUD →

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@pytorch-bot pytorch-bot Bot added ciflow/mps Run MPS tests (subset of trunk) release notes: mps Release notes category labels Jul 19, 2026
@pytorch-bot

pytorch-bot Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

Workflows were awaiting approval. CI has now been triggered for the ciflow labels on this PR.

@malfet malfet added the topic: bug fixes topic category label Jul 20, 2026

@malfet malfet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, but let's make implementation a bit more compact and do not change metallic header generation

Comment thread scripts/write_metallib_headers.py Outdated
Comment on lines +14 to +16
out_path = Path(output_filename)
out_path.parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "w") as out:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What?

@malfet malfet changed the title [MPS] Fix torch.nextafter for bfloat16 and resolve header generation FileNotFoundError [MPS] Fix torch.nextafter for bfloat16 Jul 20, 2026
Shorten the bfloat16 nextafter specialization by selecting the
increment/decrement direction with the `(from < to) == (from > 0)`
idiom instead of computing the absolute-value bit patterns. This folds
the bit-equal and +0/-0 cases into a single float `from == to` compare
and removes the nested zero sub-case, with no change in behavior
(NaN, +/-0, subnormal, and finite<->inf boundaries all match CPU).

Also revert the unrelated `write_metallib_headers.py` mkdir change so
this PR stays focused on the nextafter fix.

Test Plan:

```
python test/test_mps.py -k test_nextafter
```

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@malfet malfet added the ciflow/trunk Trigger trunk jobs on your pull request label Jul 20, 2026
@sahil-mangla
sahil-mangla requested a review from malfet July 20, 2026 08:35
@sahil-mangla

Copy link
Copy Markdown
Contributor Author

@malfet Seems like the changes applied have some cases failing do you want me to go through them and fix them?

@malfet

malfet commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@pytorchbot merge -f "MPS tests are green, lint just need a rebase"

@malfet

malfet commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@sahil-mangla those are false positives, and could be ignored

@pytorchmergebot

Copy link
Copy Markdown
Collaborator

Merge started

Your change will be merged immediately since you used the force (-f) flag, bypassing any CI checks (ETA: 1-5 minutes). Please use -f as last resort and instead consider -i/--ignore-current to continue the merge ignoring current failures. This will allow currently pending tests to finish and report signal before the merge.

Learn more about merging in the wiki.

Questions? Feedback? Please reach out to the PyTorch DevX Team

Advanced Debugging
Check the merge workflow status
here

zjliu-amd pushed a commit to zjliu-amd/pytorch that referenced this pull request Jul 21, 2026
On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`, because  `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload.
This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, closely mirroring the CPU implementation in `c10/util/BFloat16-math.h` and extend nextafter unit test to cover bfloat as well

Fixes pytorch#190476

Pull Request resolved: pytorch#190481
Approved by: https://github.com/malfet

Co-authored-by: Nikita Shulga <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
youlituao pushed a commit to youlituao/pytorch that referenced this pull request Jul 28, 2026
On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`, because  `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload.
This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, closely mirroring the CPU implementation in `c10/util/BFloat16-math.h` and extend nextafter unit test to cover bfloat as well

Fixes pytorch#190476

Pull Request resolved: pytorch#190481
Approved by: https://github.com/malfet

Co-authored-by: Nikita Shulga <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
youlituao pushed a commit to youlituao/pytorch that referenced this pull request Jul 28, 2026
On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`, because  `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload.
This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, closely mirroring the CPU implementation in `c10/util/BFloat16-math.h` and extend nextafter unit test to cover bfloat as well

Fixes pytorch#190476

Pull Request resolved: pytorch#190481
Approved by: https://github.com/malfet

Co-authored-by: Nikita Shulga <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
aws-kingrj pushed a commit to amazon-contributing/upstream-to-pytorch that referenced this pull request Jul 29, 2026
On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`, because  `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload.
This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, closely mirroring the CPU implementation in `c10/util/BFloat16-math.h` and extend nextafter unit test to cover bfloat as well

Fixes pytorch#190476

Pull Request resolved: pytorch#190481
Approved by: https://github.com/malfet

Co-authored-by: Nikita Shulga <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
MelindaTan pushed a commit to MelindaTan/pytorch that referenced this pull request Jul 31, 2026
On MPS, `torch.nextafter` was returning its input unchanged for `bfloat16`, because  `nextafter_functor` in `BinaryKernel.metal` called `::metal::nextafter`, which does not have a `bfloat` overload.
This caused the arguments to be promoted to `float`, stepped by one fp32 ULP, and then cast back to `bfloat`, which rounded the step away.

Fixed by implementing a bitwise manual stepping overload for `bfloat` inside `nextafter_functor` in `BinaryKernel.metal` using its `ushort` representation, closely mirroring the CPU implementation in `c10/util/BFloat16-math.h` and extend nextafter unit test to cover bfloat as well

Fixes pytorch#190476

Pull Request resolved: pytorch#190481
Approved by: https://github.com/malfet

Co-authored-by: Nikita Shulga <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/mps Run MPS tests (subset of trunk) ciflow/trunk Trigger trunk jobs on your pull request Merged open source release notes: mps Release notes category topic: bug fixes topic category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MPS] torch.nextafter is a no-op for bfloat16

4 participants