Skip to content

Fix mx.from_fp8 E4M3FN NaN decode with branchless carry - #4376

Open
saud5150 wants to merge 7 commits into
ml-explore:mainfrom
saud5150:fix/fp8-nan
Open

Fix mx.from_fp8 E4M3FN NaN decode with branchless carry#4376
saud5150 wants to merge 7 commits into
ml-explore:mainfrom
saud5150:fix/fp8-nan

Conversation

@saud5150

Copy link
Copy Markdown

Fixes #4135

The Bug

  • Byte 0x7f decodes to 480.0 instead of +NaN
  • Byte 0xff decodes to -480.0 instead of -NaN

The Fix

Changed decode from:
v = (x & 127) << 7; // Wrong: gives ±480.0

To:
v = x & 127; u = (v << 7) | (((v + 1) >> 7) << 14); // Right: gives ±NaN

How it works:

  • 0x7f → v = 0x7f → arithmetic sets NaN bit → +NaN
  • 0xff → v = 0x7f → arithmetic sets NaN bit → existing sign path → -NaN

Same 7 bits, sign handled by existing path. No branches, just arithmetic.

  • ☑️ I understand it is strictly prohibited to use AI to write PR description
  • AI usage disclosure: used a tool to check the bit trick and tests

Saud and others added 3 commits August 23, 2026 01:24
## The Bug

- Byte 0x7f decodes to 480.0 instead of +NaN
- Byte 0xff decodes to -480.0 instead of -NaN

## The Fix

Changed decode from:
  v = (x & 127) << 7;  // Wrong: gives ±480.0

To:
  v = x & 127;
  u = (v << 7) | (((v + 1) >> 7) << 14);  // Right: gives ±NaN

How it works:
- 0x7f → v = 0x7f → arithmetic sets NaN bit → +NaN
- 0xff → v = 0x7f → arithmetic sets NaN bit → then sign bit negates it → -NaN

Same 7 bits, sign handled by existing path. No branches, just arithmetic.
@nastya236

Copy link
Copy Markdown
Collaborator

Some tests are failing, do you mind take a look?

@nastya236 nastya236 added bug low priority await response This pull request is waiting for response from the author. labels Aug 24, 2026
@zcbenz zcbenz added await response This pull request is waiting for response from the author. and removed await response This pull request is waiting for response from the author. labels Aug 24, 2026
@saud5150

Copy link
Copy Markdown
Author

Set the NaN correctly but then negated it to flip the sign, which doesn't work. Now I will embed the sign bit directly in the fp16 encoding instead.

@zcbenz zcbenz removed the await response This pull request is waiting for response from the author. label Aug 25, 2026
@saud5150

Copy link
Copy Markdown
Author

Some tests are failing, do you mind take a look?

I will dig a bit deeper into this and add a fix.

@nastya236 nastya236 added the await response This pull request is waiting for response from the author. label Aug 25, 2026
…e code sets that sign correctly, but the × 256 step swaps the whole NaN for the hardware's canonical one, and IEEE 754 doesn't require otherwise.
@zcbenz zcbenz added await verification This pull request is non-trivial and requires a human expert to verify its correctness. and removed await response This pull request is waiting for response from the author. labels Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

await verification This pull request is non-trivial and requires a human expert to verify its correctness. bug low priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Regression: mx.from_fp8 decodes SafeTensors F8_E4M3 NaNs as finite values

3 participants