Skip to content

Declare sub-byte microfloats with their real width on Julia 1.14 - #37

Open
AntonOresten wants to merge 1 commit into
mainfrom
native-widths
Open

AntonOresten wants to merge 1 commit into
mainfrom
native-widths

Conversation

@AntonOresten

@AntonOresten AntonOresten commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

On Julia 1.14 (JuliaLang/julia#61359), a primitive type can declare any number of bits. Building on #36, this declares each microfloat with its real width there: Float4_E2M1FN is a 4-bit primitive type, Float6_E2M3FN is a 6-bit one, and so on. Each still occupies one byte. On earlier versions nothing changes, since the declared width falls back to 8.

What changes on 1.14

  • Core.bitsizeof(T) == bitwidth(T). ===, hash and objectid ignore the unused high bits. Loads and stores mask them, so junk bits can no longer be stored.
  • raw_bits/from_raw_bits use zext_int/trunc_int for sub-byte types, and keep bitcast (plus the Ignore bits above a microfloat's width #36 mask) for 8-bit types and older Julia.
  • Scalar reinterpret(UInt8, x) and reinterpret(T, ::UInt8) are defined for all microfloats. Base's bitcast would otherwise reject a 4-bit ↔ 8-bit reinterpret, so existing scalar code (e.g. reinterpret(UInt8, Float4_E2M1FN(x)) in PTX tests) keeps working. reinterpret(T, 0xf3) drops the high bits.
  • @microfloat declares primitive type T <: Microfloat N end via declared_bits(N), and its docstring and the Microfloat docstring describe both versions.

Breaking on 1.14

Array reinterpret of a sub-byte microfloat, e.g. reinterpret(UInt8, [Float4_E2M1FN(1)]), now throws ArgumentError, because Base refuses to reinterpret arrays whose elements have unused bits (Base.has_bit_padding). The fix is raw_bits.(xs) / from_raw_bits.(T, bytes), or packing through BitPacking. Since this breaks code that works today, this probably wants 0.3.0.

BitPacking 0.2.5 works as is, because it only scalar-reinterprets. MurrellGroup/BitPacking.jl#17 adds the native-width paths there too.

Tests

  • stored(U, x) reads the bytes in memory through a pointer, since reinterpret can no longer do that for sub-byte types. The vectorization and "results fit the width" tests use it.
  • New "Declared width" testset: sizeof == 1, Core.bitsizeof(T) == bitwidth(T) on 1.14, scalar reinterpret round trips including high-bit truncation, and @test_throws ArgumentError for the array case.
  • Full suite with CUDA functional (GB10): 1.14.0-alpha1 83506 pass, 1.13 83486 pass, 1.10 83486 pass. It also passes against the BitPacking#17 branch.

Codegen

The generated machine code is identical to the 8-bit build except in the scalar narrowing loop, which gains one and 15 per element (152 → 160 instructions) from the store mask.

🤖 Generated with Claude Code

Julia 1.14 lets a primitive type declare fewer bits than it stores
(JuliaLang/julia#61359). There, `@microfloat` now declares a type
narrower than a byte with its sign + exponent + significand bits, so
`Core.bitsizeof(Float4_E2M1FN) == 4`; before 1.14 it keeps 8. Values
then cannot carry bits above the width, and `===` agrees with every
other operation.

`raw_bits` and `from_raw_bits` widen and truncate such types. Base's
`reinterpret` refuses them, so on 1.14 Microfloats defines
`reinterpret` between a microfloat and `UInt8`, which keeps
single-value conversions working (dropping bits above the width), and
BitPacking 0.2.5's packing with them. Arrays of these types can no
longer be reinterpreted on 1.14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.24%. Comparing base (ac5234a) to head (84c1b83).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #37      +/-   ##
==========================================
+ Coverage   87.04%   87.24%   +0.19%     
==========================================
  Files          14       14              
  Lines         664      674      +10     
==========================================
+ Hits          578      588      +10     
  Misses         86       86              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant