Repository navigation
Fix FP8 NaN preservation in to_fp8 and E8M0 scale dequantization - #4756
Open
FaizPalwala wants to merge 1 commit into
Open
FaizPalwala wants to merge 1 commit into
FaizPalwala wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4751.
In
ToFP8(mlx/backend/cpu/unary_ops.h) andfp8_e4m3(mlx/backend/metal/kernels/fp8.h), the saturation checkif (f_bits >= fp8_max)unconditionally assigned0x7E. Because float32NaNhasf_bits > 0x7F800000 > fp8_max, all NaNs saturated to finite448.0(0x7E) instead of the standard FP8 E4M3fn NaN bit pattern (0x7F/0xFF).Rationale
0x7E/448.0), while0x7F/0xFFare reserved for NaNs. The conversion in MLX was adapted from PyTorch'sc10/util/Float8_e4m3fn.h, which explicitly gates saturation with(f_bits > 0x7F800000) ? 0x7F : 0x7E. This PR restores that check across both CPU and Metal paths.0xFFis reserved as unrepresentable / NaN. Decoding0xFFviastatic_cast<uint16_t>(bits) << 23evaluated to0x7F800000(+Infinity) in float32. This PR decodes0xFFto quiet NaN (0x7FC00000/0x7FC0) in both Metal and CPU, and adds a NaN guard infp_dequantizesopower(2.0, scales - 127)does not overflow to+infon CPU.Changes
(f_bits > 0x7F800000) ? 0x7F : 0x7Echeck in Metalfp8_e4m3and CPU SIMDToFP8prior to applying the sign bit.fp8_e8m0(Metal) anddequantize_scale(CPU), decode0xFFto quiet NaN (0x7FC00000/0x7FC0).mlx/ops.cpp:fp_dequantize, substituteNANfor0xFFscales to preventpower(2.0, 128)overflow on CPU.test_ops.pyandtest_quantized.pyvalidating NaN preservation, round-trip, overflow saturation, and E8M0 NaN scale decoding across CPU and GPU.