Shared exponent is one too large when the block maximum is just below a power of two
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Domain
- machine-learning
Research direction
Start with _shared_exponents in mx.mx_ops, reached through the reported _quantize_mx call, and compare its exponent calculation with the exact torch.frexp approach shown in the issue. Add regression coverage for the near-256 and near-FLT_MAX cases, then verify the expected outputs and finite values with custom_cuda=False.
Written by the indexing model from the issue text.
Description
Hi, I found that _shared_exponents can choose an exponent one larger than floor(log2(max)), because torch.log2 is evaluated in float32 and rounds up to exactly k for a maximum just below 2^k. OCP MX's conversion algorithm (section 6.3) uses floor(log2(max)).
import numpy as np
import torch
from mx.mx_ops import _quantize_mx
def quantize(block, fmt):
A = torch.tensor([block + [0.0] * (32 - len(block))], dtype=torch.float32)
return _quantize_mx(A, 8, fmt, shared_exp_method="max", axes=[-1], block_size=32,
round="even", flush_fp32_subnorms=False, custom_cuda=False)[0].tolist()
m = float(np.nextafter(np.float32(256), np.float32(0))) # 0x1.fffffep+7
print(torch.log2(torch.tensor(m)).item()) # 8.0
print(quantize([m, 2.0**-10], "fp8_e4m3")[:2])
# Actual: [256.0, 0.0]
# Expected: [224.0, 0.0009765625]
M = float(np.finfo(np.float32).max)
print(quantize([M, 1.0], "int8")[:2]) # Actual: [nan, nan]
print(quantize([M, 1.0], "fp8_e4m3")[:2]) # Actual: [inf, 0.0]
In the first block the scale is 2^0 instead of 2^-1, so 2^-10, which is exact at 2^-1, rounds to zero. Moving the maximum slightly away from 256 gives the expected [224.0, 0.0009765625]. Near FLT_MAX the exponent becomes 128: line 282 turns it into NaN, so a finite MXINT8 block becomes all NaN (44 of the 200 largest float32 values here), and for E4M3 the rescaling at line 292 overflows to inf instead of saturating to 448 * 2^119.
Reproduced on main at 7bc41952de394f5cc5e782baf132e7c7542eb4e4 with custom_cuda=False, Python 3.12.13 and PyTorch 2.13.0 on CPU.
Taking the exponent exactly fixes all three:
- shared_exp = torch.floor(
- torch.log2(
+ shared_exp = (torch.frexp(
shared_exp + FP32_MIN_NORMAL * (shared_exp == 0).type(shared_exp.dtype)
- )
- )
+ ).exponent - 1).to(shared_exp.dtype)
With this change the three blocks give [224.0, 0.0009765625] and finite values, and the output on 8,000 random blocks per element format is unchanged. I found this while checking FloatLib, a Lean library for the OCP MX formats, and I'm happy to send a PR with regression tests.
- Dominant language
- Python
- Stars
- 365
- Forks
- 54
- PR merge metrics
- No merged PRs in 30d
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/microxcaling
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
microsoft/microxcaling#49 ·
-
Finite FP16 MX blocks become NaNs on the default PyTorch pathPossibly taken @vera-rubin claimed this 7 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 48/100
microsoft/microxcaling#46 ·
-
Difficulty 5/5 Over a week Newbie friendliness 20/100
microsoft/microxcaling#43 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 35/100
microsoft/microxcaling#42 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 45/100
microsoft/microxcaling#36 ·
All issues in microsoft/microxcaling
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ansys/pyansys-common-mcp#213 ·
Maintainers usually reply within 3 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
freedomofpress/dangerzone#1594 ·
Maintainers usually reply within 3 days
-
priority:P1 status:in-review type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
randomparity/kdive#3317 · 1 comment ·
Maintainers usually reply within 1 day
-
area:ci
Difficulty 1/5 Under an hour Newbie friendliness 90/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
mesonbuild/meson#16281 ·
Maintainers usually reply within 1 day