Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Shared exponent is one too large when the block maximum is just below a power of two

Open Beginner friendly
#48 0 comments 0 reactions 0 assignees View on GitHub

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
Tech stack
python, pytorch

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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/microxcaling

All issues in microsoft/microxcaling

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.