Skip to content

Commit 1eef1d1

Browse files
authored
Metal/CPU nvfp4 and mxfp8 (#2946)
1 parent 9cfda1a commit 1eef1d1

14 files changed

Lines changed: 665 additions & 541 deletions

File tree

mlx/backend/cpu/quantized.cpp

Lines changed: 158 additions & 66 deletions
Large diffs are not rendered by default.

mlx/backend/metal/kernels/fp4.h

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
#pragma once
22

3-
constexpr constant static float FP4_LUT[16] = {
4-
+0.0f,
5-
+0.5f,
6-
+1.0f,
7-
+1.5f,
8-
+2.0f,
9-
+3.0f,
10-
+4.0f,
11-
+6.0f,
12-
-0.0f,
13-
-0.5f,
14-
-1.0f,
15-
-1.5f,
16-
-2.0f,
17-
-3.0f,
18-
-4.0f,
19-
-6.0f};
20-
213
struct fp4_e2m1 {
224
fp4_e2m1(float x) {
235
if (metal::isnan(x)) {
@@ -48,11 +30,18 @@ struct fp4_e2m1 {
4830
bits |= sign_bit;
4931
}
5032

51-
operator float() {
33+
operator float16_t() {
5234
half converted = as_type<half>(ushort((bits & 7) << 9));
5335
converted *= 16384.0;
54-
converted = bits & 8 ? -converted : converted;
55-
return converted;
36+
return bits & 8 ? -converted : converted;
37+
}
38+
39+
operator float() {
40+
return static_cast<float>(this->operator float16_t());
41+
}
42+
43+
operator bfloat16_t() {
44+
return static_cast<bfloat16_t>(this->operator float16_t());
5645
}
5746

5847
uint8_t bits;

mlx/backend/metal/kernels/fp8.h

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,20 @@ struct fp8_e4m3 {
2929
bits |= static_cast<uint8_t>(sign >> 24);
3030
}
3131

32-
operator float() {
33-
// From PyTorch:
34-
// https://github.com/pytorch/pytorch/blob/e3643e1e0e923f0fc063dfab6f45c956d568919d/c10/util/Float8_e4m3fn.h#L46
35-
uint32_t w = static_cast<uint32_t>(bits) << 24;
36-
uint32_t sign = w & 0x80000000;
37-
uint32_t nonsign = w & 0x7FFFFFFF;
32+
operator float16_t() {
33+
uint16_t v = (bits & 127) << 7;
34+
half converted = as_type<half>(v);
35+
converted *= 256.0;
36+
auto sign = bits & 128;
37+
return (sign ? -converted : converted);
38+
}
3839

39-
uint32_t renorm_shift = metal::clz(nonsign);
40-
renorm_shift = renorm_shift > 4 ? renorm_shift - 4 : 0;
40+
operator bfloat16_t() {
41+
return static_cast<bfloat16_t>(this->operator float16_t());
42+
}
4143

42-
int32_t inf_nan_mask =
43-
(static_cast<int32_t>(nonsign + 0x01000000) >> 8) & 0x7F800000;
44-
int32_t zero_mask = static_cast<int32_t>(nonsign - 1) >> 31;
45-
uint32_t result = sign |
46-
((((nonsign << renorm_shift >> 4) + ((0x78 - renorm_shift) << 23)) |
47-
inf_nan_mask) &
48-
~zero_mask);
49-
return as_type<float>(result);
44+
operator float() {
45+
return static_cast<float>(this->operator float16_t());
5046
}
5147

5248
uint8_t bits;
@@ -74,8 +70,10 @@ struct fp8_e8m0 {
7470
uint16_t out = (bits == 0 ? 0x40 : (static_cast<uint16_t>(bits) << 7));
7571
return as_type<bfloat16_t>(out);
7672
}
73+
7774
operator float() {
78-
return static_cast<float>(this->operator bfloat16_t());
75+
uint32_t out = (bits == 0 ? 0x400000 : (static_cast<uint16_t>(bits) << 23));
76+
return as_type<float>(out);
7977
}
8078

8179
uint8_t bits;

0 commit comments

Comments
 (0)