# Scalar Types

> NumKong for C

The scalar wrappers in `include/numkong/types.hpp` are storage-first types.
They encode raw layout, default output types, and the kernel function pointer signatures for each family.

| Type          | Layout           | Bytes |           Range | Inf | NaN |
| :------------ | :--------------- | ----: | --------------: | :-: | :-: |
| `nk_f16_t`    | 1+5+10           |     2 |          ±65504 | yes | yes |
| `nk_bf16_t`   | 1+8+7            |     2 |       ±3.4×10³⁸ | yes | yes |
| `nk_e4m3_t`   | 1+4+3            |     1 |            ±448 | no  | yes |
| `nk_e5m2_t`   | 1+5+2            |     1 |          ±57344 | yes | yes |
| `nk_e2m3_t`   | 1+2+3            |     1 |            ±7.5 | no  | no  |
| `nk_e3m2_t`   | 1+3+2            |     1 |             ±28 | no  | no  |
| `nk_e2m1x2_t` | 2 × (1+2+1)      |     1 |   ±6 per nibble | no  | no  |
| `nk_u1x8_t`   | 8 packed bits    |     1 |  0 or 1 per bit | n/a | n/a |
| `nk_u4x2_t`   | 2 × 4-bit        |     1 | 0…15 per nibble | n/a | n/a |
| `nk_i4x2_t`   | 2 × 4-bit signed |     1 | −8…7 per nibble | n/a | n/a |

The layout column shows sign, exponent, and mantissa bit counts for floating-point types.
For `nk_f16_t`, 1+5+10 means one sign bit, five exponent bits, and ten mantissa bits, totaling 16 bits stored in 2 bytes.
For `nk_bf16_t`, the wider exponent field (8 bits) gives the same dynamic range as IEEE 754 single precision but with reduced mantissa precision.
The Float8 types `nk_e4m3_t` and `nk_e5m2_t` follow the OFP8 specification.
The narrower `nk_e2m3_t` and `nk_e3m2_t` types are MX-compatible micro-floats, and `nk_e2m1x2_t` packs two FP4 values of the MXFP4 and NVFP4 formats.
Sub-byte types `nk_e2m1x2_t`, `nk_u1x8_t`, `nk_u4x2_t`, and `nk_i4x2_t` pack multiple logical values into a single byte.
Element 0 sits in the high nibble or the most significant bit, and every dimension count must be a multiple of the values per byte.

Default promotions are encoded on the type.
For example, `f32_t::dot_result_t` is wider than `f32_t`.
`i8_t::dot_result_t` is `i32_t`.
`u1x8_t::dot_result_t` is `u32_t`.

The higher-level templates use `result_type_ = typename in_type_::dot_result_t` and similar defaults.
The fast typed overloads are constrained so that overriding the result type away from the native policy can disable the specialized path and fall back to the more generic one.

When `__cpp_lib_format >= 202110L` for the C++23 `<format>` header support, all NumKong scalar types provide `std::formatter` specializations with similar format specs to the traditional `float`.
For the Float16 type, the output for `nk::f16_t::from_f32(3.14f)` will look like:

| Format spec | Output example       | Description                            |
| :---------- | :------------------- | :------------------------------------- |
| `{}`        | `3.140625`           | Clean float value                      |
| `{:#}`      | `3.140625 [0x4248]`  | Annotated with hex bits                |
| `{:.2f}`    | `3.14`               | Precision forwarded to float formatter |
| `{:x}`      | `4248`               | Raw hex bits                           |
| `{:#x}`     | `0x4248`             | Hex with prefix                        |
| `{:X}`      | `4248`               | Uppercase hex                          |
| `{:b}`      | `0100001001001000`   | Binary bits                            |
| `{:#b}`     | `0b0100001001001000` | Binary with prefix                     |

`#include <numkong/scalar.h>`

`#include <numkong/cast.h>`

`#include <numkong/types.h>`

## nk_f32_unary_punned_t

Scalar kernels of one shape, cast back from `[nk_kernel_punned_t](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_kernel_punned_t)` by their dispatch points.

```c
typedef nk_f32_t(*) nk_f32_unary_punned_t(nk_f32_t);
```

## nk_f16_to_f32_punned_t

Scalar conversion kernels of one shape, cast back from `[nk_kernel_punned_t](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_kernel_punned_t)` by their dispatch points; the FP8 and FP6 codes share the byte shape.

```c
typedef void(*) nk_f16_to_f32_punned_t(nk_f16_t const *, nk_f32_t *);
```

## nk_u1x8_t

Packed 8-bit bit-vector (8 booleans in one byte), MSB = dimension 0.

```c
typedef unsigned char nk_u1x8_t;
```

Used for Hamming distance and Jaccard similarity via popcount. Dimension count must be a multiple of 8, the values per byte.

## nk_i4x2_t

Packed 4-bit signed integer pair (2 × i4 in one byte), [high nibble : low nibble].

```c
typedef unsigned char nk_i4x2_t;
```

Range per element: [−8, +7]. Elements sign-extended to i8 for arithmetic. Dimension count must be a multiple of 2, the values per byte.

## nk_u4x2_t

Packed 4-bit unsigned integer pair (2 × u4 in one byte), [high nibble : low nibble].

```c
typedef unsigned char nk_u4x2_t;
```

Range per element: [0, 15]. Elements zero-extended to u8 for arithmetic. Dimension count must be a multiple of 2, the values per byte.

## nk_e4m3_t

8-bit E4M3 float (OCP FP8): sign(1) + exponent(4) + mantissa(3), bias=7.

```c
typedef unsigned char nk_e4m3_t;
```

Range: ±448, no infinities (all-ones exponent → NaN at 0x7F/0xFF). 114 of 254 finite values, or 44.9%, fall in [−1, +1].

## nk_e5m2_t

8-bit E5M2 float (OCP FP8): sign(1) + exponent(5) + mantissa(2), bias=15.

```c
typedef unsigned char nk_e5m2_t;
```

Range: ±57 344, supports infinities at 0x7C/0xFC. 122 of 248 finite values (49.2%) fall in [−1, +1].

## nk_e2m3_t

6-bit E2M3 micro-float (OCP MX v1.0): sign(1) + exponent(2) + mantissa(3), bias=1.

```c
typedef unsigned char nk_e2m3_t;
```

Stored as 0b00SEEMMM with 2 bits of padding. Range: ±7.5, no infinities or NaN. 64 total codes: 48 normal, 14 subnormal (exp=0, mant ≠ 0), 2 zeros (±0). 18 of 64 values (28.1%) fall in [−1, +1]. Subnormal values span [±0.125, ±0.875]. Losslessly promotable to E4M3 by rebiasing exponent +6 (normals) or normalizing (subnormals).

## nk_e3m2_t

6-bit E3M2 micro-float (OCP MX v1.0): sign(1) + exponent(3) + mantissa(2), bias=3.

```c
typedef unsigned char nk_e3m2_t;
```

Stored as 0b00SEEEMM with 2 bits of padding. Range: ±28, no infinities or NaN. 64 total codes: 56 normal, 6 subnormal (exp=0, mant ≠ 0), 2 zeros (±0). 26 of 64 values (40.6%) fall in [−1, +1]. Subnormal values span [±0.0625, ±0.1875]. Losslessly promotable to E5M2 by rebiasing exponent +12 (normals) or normalizing (subnormals).

## nk_e2m1x2_t

Packed 4-bit E2M1 micro-float pair (2 × e2m1 in one byte), [high nibble : low nibble].

```c
typedef unsigned char nk_e2m1x2_t;
```

OCP MX v1.0 sub-format: sign(1) + exponent(2) + mantissa(1), bias=1. Range: ±6.0, no Inf or NaN. 16 total codes: 8 magnitudes {0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0} × 2 signs (two zeros). Used as the element type of MXFP4 (block=32, UE8M0 scales) and NVFP4 (block=16, UE4M3 scales). Dimension count must be a multiple of 2, the values per byte.

## nk_ue8m0_t

Unsigned 8-bit power-of-two scale (OCP MX v1.0): 8-bit biased exponent, no sign or mantissa.

```c
typedef unsigned char nk_ue8m0_t;
```

Encodes 2^(v - 127) for v ∈ [1, 254]; v = 0 is zero; v = 255 is the NaN-block sentinel. Used as the per-block scale byte for MXFP4, MXFP6, MXFP8, MXINT8.

## nk_ue4m3_t

Unsigned 8-bit E4M3 scale (NVFP4): sign-bit forced to 0, otherwise identical to E4M3.

```c
typedef unsigned char nk_ue4m3_t;
```

Range: [0, +448]. Used as the per-block scale byte for NVFP4 (block=16) alongside an f32 "tensor scale".

## nk_i8_t

Signed 8-bit integer.

```c
typedef signed char nk_i8_t;
```

Range: [−128, +127].

## nk_u8_t

Unsigned 8-bit integer.

```c
typedef unsigned char nk_u8_t;
```

Range: [0, 255].

## nk_i16_t

Signed 16-bit integer.

```c
typedef signed short nk_i16_t;
```

Range: [−32 768, +32 767].

## nk_u16_t

Unsigned 16-bit integer.

```c
typedef unsigned short nk_u16_t;
```

Range: [0, 65 535].

## nk_i32_t

Signed 32-bit integer.

```c
typedef signed int nk_i32_t;
```

Range: [−2³¹, +2³¹−1].

## nk_u32_t

Unsigned 32-bit integer.

```c
typedef unsigned int nk_u32_t;
```

Range: [0, 2³²−1].

## nk_i64_t

Signed 64-bit integer.

```c
typedef signed long long nk_i64_t;
```

Range: [−2⁶³, +2⁶³−1].

## nk_u64_t

Unsigned 64-bit integer.

```c
typedef unsigned long long nk_u64_t;
```

Range: [0, 2⁶⁴−1].

## nk_f32_t

Single-precision (32-bit) IEEE 754 float.

```c
typedef float nk_f32_t;
```

sign(1) + exponent(8) + mantissa(23), bias=127.

## nk_f64_t

Double-precision (64-bit) IEEE 754 float.

```c
typedef double nk_f64_t;
```

sign(1) + exponent(11) + mantissa(52), bias=1023.

## nk_dtype_t

Enumeration of supported scalar data types.

```c
enum nk_dtype_t
```

Includes complex type descriptors which in C code would use the real counterparts, but the independent flags contain metadata to be passed between programming language interfaces.

Block-scaled formats are composites, `element_dtype | scale_dtype`: two bits set, so they never collide with each other or with a singleton. The scale dtype fixes the block, 16 for UE4M3 in NVFP4 and 32 for UE8M0 in the MX family.

- `nk_dtype_unknown_k` = `0`: Unknown data type.
- `nk_u1_k` = `1 << 1`: Single-bit values packed into 8-bit words.
- `nk_i8_k` = `1 << 2`: 8-bit signed integer.
- `nk_i16_k` = `1 << 3`: 16-bit signed integer.
- `nk_i32_k` = `1 << 4`: 32-bit signed integer.
- `nk_i64_k` = `1 << 5`: 64-bit signed integer.
- `nk_u8_k` = `1 << 6`: 8-bit unsigned integer.
- `nk_u16_k` = `1 << 7`: 16-bit unsigned integer.
- `nk_u32_k` = `1 << 8`: 32-bit unsigned integer.
- `nk_u64_k` = `1 << 9`: 64-bit unsigned integer.
- `nk_f64_k` = `1 << 10`: Double precision floating point.
- `nk_f32_k` = `1 << 11`: Single precision floating point.
- `nk_f16_k` = `1 << 12`: Half precision floating point.
- `nk_bf16_k` = `1 << 13`: Brain floating point.
- `nk_e4m3_k` = `1 << 14`: FP8 E4M3 floating point.
- `nk_e5m2_k` = `1 << 15`: FP8 E5M2 floating point.
- `nk_i4_k` = `1 << 16`: 4-bit signed integers packed into 8-bit words.
- `nk_u4_k` = `1 << 17`: 4-bit unsigned integers packed into 8-bit words.
- `nk_e2m3_k` = `1 << 18`: FP6 E2M3 floating point.
- `nk_e3m2_k` = `1 << 19`: FP6 E3M2 floating point.
- `nk_f64c_k` = `1 << 20`: Complex double precision floating point.
- `nk_f32c_k` = `1 << 21`: Complex single precision floating point.
- `nk_f16c_k` = `1 << 22`: Complex half precision floating point.
- `nk_bf16c_k` = `1 << 23`: Complex brain floating point.
- `nk_e2m1_k` = `1 << 24`: FP4 E2M1 floating point (element of MXFP4 and NVFP4).
- `nk_ue8m0_k` = `1 << 25`: UE8M0 unsigned pow-2 scale byte (MX family block scale).
- `nk_ue4m3_k` = `1 << 26`: UE4M3 unsigned E4M3 scale byte (NVFP4 block scale).
- `nk_nvfp4_k` = `nk_e2m1_k | nk_ue4m3_k`: NVIDIA NVFP4 (block=16, f32 tensor scale).
- `nk_mxfp4_k` = `nk_e2m1_k | nk_ue8m0_k`: OCP MXFP4 (block=32).
- `nk_mxfp6e2m3_k` = `nk_e2m3_k | nk_ue8m0_k`: OCP MXFP6 (E2M3 variant, block=32).
- `nk_mxfp6e3m2_k` = `nk_e3m2_k | nk_ue8m0_k`: OCP MXFP6 (E3M2 variant, block=32).
- `nk_mxfp8e4m3_k` = `nk_e4m3_k | nk_ue8m0_k`: OCP MXFP8 (E4M3 variant, block=32).
- `nk_mxfp8e5m2_k` = `nk_e5m2_k | nk_ue8m0_k`: OCP MXFP8 (E5M2 variant, block=32).
- `nk_mxint8_k` = `nk_i8_k | nk_ue8m0_k`: OCP MXINT8 (block=32).

Spellings: ```
char const * nk_dtype_name(nk_dtype_t dtype);
```

## nk_block_scaled_format_t

Descriptor for a block-scaled tensor layout (OCP MX family + NVIDIA NVFP4).

```c
struct nk_block_scaled_format_t
```

Elements are grouped in fixed-size contiguous blocks; each block has its own `scale_dtype` scale byte stored in a separate scales buffer. An optional `tensor_scale_dtype` scalar multiplier applies to the whole tensor (NVFP4's per-tensor FP32 factor; absent for the MX family).

Plain (non-block-scaled) buffers are described by `scale_dtype = [nk_dtype_unknown_k](#nk_dtype_unknown_k)`, `tensor_scale_dtype = [nk_dtype_unknown_k](#nk_dtype_unknown_k)`, and `block_size = 0` — use the `nk_plain()` factory.

- `element_dtype` `nk_dtype_t`: Per-element dtype: e2m1/e4m3/e5m2/e2m3/e3m2/i8 (or any for plain).
- `scale_dtype` `nk_dtype_t`: Per-block scale: ue8m0 (MX) or ue4m3 (NVFP4); unknown for plain.
- `tensor_scale_dtype` `nk_dtype_t`: Per-tensor multiplier: f32 (NVFP4) or unknown (MX, plain).
- `block_size` `nk_size_t`: Elements per block: 16 (NVFP4) or 32 (MX); 0 for plain.
- `block_bytes` `nk_size_t`: Bytes of codes per block, dividing a row stride into a scale row stride; 0 for plain.

## nk_nvfp4_cref_t

A read-only NVFP4 scalar: its E2M1 code, its block's UE4M3 scale, and the tensor's F32 scale.

```c
struct nk_nvfp4_cref_t
```

- `elements` `nk_e2m1x2_t const *`
- `scales` `nk_ue4m3_t const *`
- `tensor_scale` `nk_f32_t const *`

## nk_nvfp4_ref_t

The writable twin of `[nk_nvfp4_cref_t](#nk_nvfp4_cref_t)`.

```c
struct nk_nvfp4_ref_t
```

- `elements` `nk_e2m1x2_t *`
- `scales` `nk_ue4m3_t *`
- `tensor_scale` `nk_f32_t *`

## nk_mxfp4_cref_t

A read-only MXFP4 scalar: its E2M1 code, its block's UE8M0 scale.

```c
struct nk_mxfp4_cref_t
```

- `elements` `nk_e2m1x2_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxfp4_ref_t

The writable twin of `[nk_mxfp4_cref_t](#nk_mxfp4_cref_t)`.

```c
struct nk_mxfp4_ref_t
```

- `elements` `nk_e2m1x2_t *`
- `scales` `nk_ue8m0_t *`

## nk_mxfp6e2m3_cref_t

A read-only MXFP6 E2M3 scalar: its E2M3 code, its block's UE8M0 scale.

```c
struct nk_mxfp6e2m3_cref_t
```

- `elements` `nk_e2m3_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxfp6e2m3_ref_t

The writable twin of `[nk_mxfp6e2m3_cref_t](#nk_mxfp6e2m3_cref_t)`.

```c
struct nk_mxfp6e2m3_ref_t
```

- `elements` `nk_e2m3_t *`
- `scales` `nk_ue8m0_t *`

## nk_mxfp6e3m2_cref_t

A read-only MXFP6 E3M2 scalar: its E3M2 code, its block's UE8M0 scale.

```c
struct nk_mxfp6e3m2_cref_t
```

- `elements` `nk_e3m2_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxfp6e3m2_ref_t

The writable twin of `[nk_mxfp6e3m2_cref_t](#nk_mxfp6e3m2_cref_t)`.

```c
struct nk_mxfp6e3m2_ref_t
```

- `elements` `nk_e3m2_t *`
- `scales` `nk_ue8m0_t *`

## nk_mxfp8e4m3_cref_t

A read-only MXFP8 E4M3 scalar: its E4M3 code, its block's UE8M0 scale.

```c
struct nk_mxfp8e4m3_cref_t
```

- `elements` `nk_e4m3_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxfp8e4m3_ref_t

The writable twin of `[nk_mxfp8e4m3_cref_t](#nk_mxfp8e4m3_cref_t)`.

```c
struct nk_mxfp8e4m3_ref_t
```

- `elements` `nk_e4m3_t *`
- `scales` `nk_ue8m0_t *`

## nk_mxfp8e5m2_cref_t

A read-only MXFP8 E5M2 scalar: its E5M2 code, its block's UE8M0 scale.

```c
struct nk_mxfp8e5m2_cref_t
```

- `elements` `nk_e5m2_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxfp8e5m2_ref_t

The writable twin of `[nk_mxfp8e5m2_cref_t](#nk_mxfp8e5m2_cref_t)`.

```c
struct nk_mxfp8e5m2_ref_t
```

- `elements` `nk_e5m2_t *`
- `scales` `nk_ue8m0_t *`

## nk_mxint8_cref_t

A read-only MXINT8 scalar: its I8 code, its block's UE8M0 scale.

```c
struct nk_mxint8_cref_t
```

- `elements` `nk_i8_t const *`
- `scales` `nk_ue8m0_t const *`

## nk_mxint8_ref_t

The writable twin of `[nk_mxint8_cref_t](#nk_mxint8_cref_t)`.

```c
struct nk_mxint8_ref_t
```

- `elements` `nk_i8_t *`
- `scales` `nk_ue8m0_t *`

## nk_dtype_family_t

The kind of number a data type holds, as [`nk_dtype_family`](#nk_dtype_family) reports it.

```c
enum nk_dtype_family_t
```

- `nk_dtype_family_unknown_k` = `0`
- `nk_dtype_family_float_k`
- `nk_dtype_family_complex_float_k`
- `nk_dtype_family_int_k`
- `nk_dtype_family_uint_k`

## nk_nvfp4_t

Alias for the half-precision floating-point type on Arm.

```c
struct nk_nvfp4_t
```

Clang and GCC bring the `float16_t` symbol when you compile for AArch64. MSVC lacks it, and it's `vld1_f16-like` intrinsics are in reality macros, that cast to 16-bit integers internally, instead of using floats. Some of those are defined as aliases, so we use `#define` preprocessor directives instead of `typedef` to avoid errors.

Block-scaled composite POD types — one value is one whole block.

Each composite combines a packed element buffer with its per-block scale byte in a single POD struct. The struct IS the value: `sizeof(nk_nvfp4_t) == 9` means one NVFP4 value, not one element. Storage in containers strides by `sizeof(struct)`; logical element count per value is reported by `nk_dimensions_per_value(composite_dtype)` (16 for NVFP4, 32 for MX). Dimension count must be a multiple of that block size.

The NVFP4 per-tensor f32 tensor scale multiplier is not part of the block value — it lives on the enclosing tensor and is passed explicitly to encode/decode helpers.

## nk_mxfp4_t

One OCP MXFP4 block: 32 E2M1 elements sharing a UE8M0 scale.

```c
struct nk_mxfp4_t
```

## nk_mxfp6e2m3_t

One OCP MXFP6 E2M3 block: 32 E2M3 elements sharing a UE8M0 scale.

```c
struct nk_mxfp6e2m3_t
```

## nk_mxfp6e3m2_t

One OCP MXFP6 E3M2 block: 32 E3M2 elements sharing a UE8M0 scale.

```c
struct nk_mxfp6e3m2_t
```

## nk_mxfp8e4m3_t

One OCP MXFP8 E4M3 block: 32 E4M3 elements sharing a UE8M0 scale.

```c
struct nk_mxfp8e4m3_t
```

## nk_mxfp8e5m2_t

One OCP MXFP8 E5M2 block: 32 E5M2 elements sharing a UE8M0 scale.

```c
struct nk_mxfp8e5m2_t
```

## nk_mxint8_t

One OCP MXINT8 block: 32 I8 elements sharing a UE8M0 scale.

```c
struct nk_mxint8_t
```

## nk_fui16_t

16-bit union for f16/bf16/u16/i16 bit manipulation.

```c
union nk_fui16_t
```

- `u` `nk_u16_t`
- `i` `nk_i16_t`
- `f` `nk_f16_t`
- `bf` `nk_bf16_t`

## nk_fui32_t

32-bit union for f32/u32/i32 bit manipulation.

```c
union nk_fui32_t
```

- `u` `nk_u32_t`
- `i` `nk_i32_t`
- `f` `nk_f32_t`

## nk_fui64_t

64-bit union for f64/u64/i64 bit manipulation.

```c
union nk_fui64_t
```

- `u` `nk_u64_t`
- `i` `nk_i64_t`
- `f` `nk_f64_t`

## nk_f16c_t

Half-precision (32-bit) complex number — {real: f16, imag: f16}.

```c
struct nk_f16c_t
```

Kernel outputs widened to f32c.

- `real` `nk_f16_t`
- `imag` `nk_f16_t`

## nk_bf16c_t

BFloat16 (32-bit) complex number — {real: bf16, imag: bf16}.

```c
struct nk_bf16c_t
```

Kernel outputs widened to f32c.

- `real` `nk_bf16_t`
- `imag` `nk_bf16_t`

## nk_f32c_t

Single-precision (64-bit) complex number — {real: f32, imag: f32}.

```c
struct nk_f32c_t
```

- `real` `nk_f32_t`
- `imag` `nk_f32_t`

## nk_f64c_t

Double-precision (128-bit) complex number — {real: f64, imag: f64}.

```c
struct nk_f64c_t
```

- `real` `nk_f64_t`
- `imag` `nk_f64_t`

## nk_b32_vec_t

Small 4-byte memory slice viewable as different types.

```c
union nk_b32_vec_t
```

- `u32` `nk_u32_t`
- `i32` `nk_i32_t`
- `f32` `nk_f32_t`
- `u8s` `nk_u8_t`
- `i8s` `nk_i8_t`
- `u16s` `nk_u16_t`
- `i16s` `nk_i16_t`
- `e4m3s` `nk_e4m3_t`
- `e5m2s` `nk_e5m2_t`

## nk_b64_vec_t

Small 8-byte memory slice viewable as different types.

```c
union nk_b64_vec_t
```

- `u8s` `nk_u8_t`
- `u16s` `nk_u16_t`
- `u32s` `nk_u32_t`
- `u64` `nk_u64_t`
- `i8s` `nk_i8_t`
- `i16s` `nk_i16_t`
- `i32s` `nk_i32_t`
- `i64` `nk_i64_t`
- `f16s` `nk_f16_t`
- `bf16s` `nk_bf16_t`
- `f32s` `nk_f32_t`

## nk_b128_vec_t

Small 16-byte memory slice viewable as different types.

```c
union nk_b128_vec_t
```

- `u8s` `nk_u8_t`
- `u16s` `nk_u16_t`
- `u32s` `nk_u32_t`
- `u64s` `nk_u64_t`
- `i8s` `nk_i8_t`
- `i16s` `nk_i16_t`
- `i32s` `nk_i32_t`
- `i64s` `nk_i64_t`
- `f16s` `nk_f16_t`
- `bf16s` `nk_bf16_t`
- `e4m3s` `nk_e4m3_t`
- `e5m2s` `nk_e5m2_t`
- `e2m3s` `nk_e2m3_t`
- `e3m2s` `nk_e3m2_t`
- `f32s` `nk_f32_t`
- `f64s` `nk_f64_t`

## nk_b256_vec_t

Small 32-byte memory slice viewable as different types.

```c
union nk_b256_vec_t
```

- `u8s` `nk_u8_t`
- `u16s` `nk_u16_t`
- `u32s` `nk_u32_t`
- `u64s` `nk_u64_t`
- `i8s` `nk_i8_t`
- `i16s` `nk_i16_t`
- `i32s` `nk_i32_t`
- `i64s` `nk_i64_t`
- `f16s` `nk_f16_t`
- `bf16s` `nk_bf16_t`
- `e4m3s` `nk_e4m3_t`
- `e5m2s` `nk_e5m2_t`
- `e2m3s` `nk_e2m3_t`
- `e3m2s` `nk_e3m2_t`
- `f32s` `nk_f32_t`
- `f64s` `nk_f64_t`

## nk_b512_vec_t

Small 64-byte memory slice viewable as different types.

```c
union nk_b512_vec_t
```

TODO: On GCC and Clang we use `__transparent_union__` attribute to allow implicit conversions between the different vector types when passing them as function arguments. The most important side-effect of this is that the argument of such type is passed to functions using the calling convention of the first member of the union, which in our case is a register-based calling convention for SIMD types.

- `u8s` `nk_u8_t`
- `u16s` `nk_u16_t`
- `u32s` `nk_u32_t`
- `u64s` `nk_u64_t`
- `i8s` `nk_i8_t`
- `i16s` `nk_i16_t`
- `i32s` `nk_i32_t`
- `i64s` `nk_i64_t`
- `f16s` `nk_f16_t`
- `bf16s` `nk_bf16_t`
- `f32s` `nk_f32_t`
- `f64s` `nk_f64_t`
- `e4m3s` `nk_e4m3_t`
- `e5m2s` `nk_e5m2_t`
- `e2m3s` `nk_e2m3_t`
- `e3m2s` `nk_e3m2_t`

## nk_scalar_buffer_t

Union for type-punned scalar values at language binding boundaries.

```c
union nk_scalar_buffer_t
```

Bridges different type systems (Python, JavaScript, etc.) where scalars arrive as f64 but need to be passed to kernels as typed pointers. Callers fill the appropriate union member based on the target dtype, then pass the union address as `void const *`.

- `bytes` `nk_u8_t`
- `f64` `nk_f64_t`
- `f32` `nk_f32_t`
- `f16` `nk_f16_t`
- `bf16` `nk_bf16_t`
- `f64c` `nk_f64c_t`
- `f32c` `nk_f32c_t`
- `f16c` `nk_f16c_t`
- `bf16c` `nk_bf16c_t`
- `i64` `nk_i64_t`
- `u64` `nk_u64_t`
- `i32` `nk_i32_t`
- `u32` `nk_u32_t`
- `i16` `nk_i16_t`
- `u16` `nk_u16_t`
- `i8` `nk_i8_t`
- `u8` `nk_u8_t`

## nk_*_sqrt_best

Scalar square root, √x.

```c
nk_f32_t nk_f32_sqrt_best(nk_f32_t x, nk_capability_t capabilities);
```

- `nk_f32_sqrt_best`: x `nk_f32_t`
- `nk_f64_sqrt_best`: x `nk_f64_t`
- `nk_f16_sqrt_best`: x `nk_f16_t`

- `nk_f32_sqrt` backends: serial, neon, haswell, rvv, v128, powervsx, loongsonasx
- `nk_f64_sqrt` backends: serial, neon, haswell, rvv, v128, powervsx, loongsonasx
- `nk_f16_sqrt` backends: serial, neonhalf, haswell, sapphire

- `x` (in): The input value.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

## nk_*_rsqrt_best

Scalar reciprocal square root, 1 / √x.

```c
nk_f32_t nk_f32_rsqrt_best(nk_f32_t x, nk_capability_t capabilities);
```

- `nk_f32_rsqrt_best`: x `nk_f32_t`
- `nk_f64_rsqrt_best`: x `nk_f64_t`
- `nk_f16_rsqrt_best`: x `nk_f16_t`

- `nk_f32_rsqrt` backends: serial, neon, haswell, rvv, v128, powervsx, loongsonasx
- `nk_f64_rsqrt` backends: serial, neon, haswell, rvv, v128, powervsx, loongsonasx
- `nk_f16_rsqrt` backends: serial, neonhalf, haswell, sapphire

- `x` (in): The input value.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: C++ `std::rsqrt`

See also: Rust `f32::rsqrt`

## nk_*_saturating_add_best

Saturating addition clamped to the representable range of the type.

```c
nk_u8_t nk_u8_saturating_add_best(
    nk_u8_t a,
    nk_u8_t b,
    nk_capability_t capabilities);
```

- `nk_u8_saturating_add_best`: a, b `nk_u8_t`
- `nk_i8_saturating_add_best`: a, b `nk_i8_t`
- `nk_u16_saturating_add_best`: a, b `nk_u16_t`
- `nk_i16_saturating_add_best`: a, b `nk_i16_t`
- `nk_u32_saturating_add_best`: a, b `nk_u32_t`
- `nk_i32_saturating_add_best`: a, b `nk_i32_t`
- `nk_u64_saturating_add_best`: a, b `nk_u64_t`
- `nk_i64_saturating_add_best`: a, b `nk_i64_t`
- `nk_i4x2_saturating_add_best`: a, b `nk_i4x2_t`
- `nk_u4x2_saturating_add_best`: a, b `nk_u4x2_t`

- `nk_u8_saturating_add` backends: serial, neon, haswell, rvv
- `nk_i8_saturating_add` backends: serial, neon, haswell, rvv
- `nk_u16_saturating_add` backends: serial, neon, haswell, rvv
- `nk_i16_saturating_add` backends: serial, neon, haswell, rvv
- `nk_u32_saturating_add` backends: serial, neon, rvv
- `nk_i32_saturating_add` backends: serial, neon, rvv
- `nk_u64_saturating_add` backends: serial, neon, rvv
- `nk_i64_saturating_add` backends: serial, neon, rvv
- `nk_i4x2_saturating_add` backends: serial
- `nk_u4x2_saturating_add` backends: serial

- `a` (in): First operand.
- `b` (in): Second operand.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

## nk_*_saturating_mul_best

Saturating multiplication clamped to the representable range of the type.

```c
nk_u8_t nk_u8_saturating_mul_best(
    nk_u8_t a,
    nk_u8_t b,
    nk_capability_t capabilities);
```

- `nk_u8_saturating_mul_best`: a, b `nk_u8_t`
- `nk_i8_saturating_mul_best`: a, b `nk_i8_t`
- `nk_u16_saturating_mul_best`: a, b `nk_u16_t`
- `nk_i16_saturating_mul_best`: a, b `nk_i16_t`
- `nk_u32_saturating_mul_best`: a, b `nk_u32_t`
- `nk_i32_saturating_mul_best`: a, b `nk_i32_t`
- `nk_u64_saturating_mul_best`: a, b `nk_u64_t`
- `nk_i64_saturating_mul_best`: a, b `nk_i64_t`
- `nk_i4x2_saturating_mul_best`: a, b `nk_i4x2_t`
- `nk_u4x2_saturating_mul_best`: a, b `nk_u4x2_t`

- `nk_u8_saturating_mul` backends: serial, rvv
- `nk_i8_saturating_mul` backends: serial, rvv
- `nk_u16_saturating_mul` backends: serial, rvv
- `nk_i16_saturating_mul` backends: serial, rvv
- `nk_u32_saturating_mul` backends: serial, rvv
- `nk_i32_saturating_mul` backends: serial, rvv
- `nk_u64_saturating_mul` backends: serial, neon, haswell, rvv
- `nk_i64_saturating_mul` backends: serial, neon, haswell, rvv
- `nk_i4x2_saturating_mul` backends: serial
- `nk_u4x2_saturating_mul` backends: serial

- `a` (in): First operand.
- `b` (in): Second operand.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

## nk_*_order_best

Branchless sign-magnitude ordering for non-native floating-point scalars.

```c
int nk_f16_order_best(
    nk_f16_t a,
    nk_f16_t b,
    nk_capability_t capabilities);
```

- `nk_f16_order_best`: a, b `nk_f16_t`
- `nk_bf16_order_best`: a, b `nk_bf16_t`
- `nk_e4m3_order_best`: a, b `nk_e4m3_t`
- `nk_e5m2_order_best`: a, b `nk_e5m2_t`
- `nk_e2m3_order_best`: a, b `nk_e2m3_t`
- `nk_e3m2_order_best`: a, b `nk_e3m2_t`

- `nk_f16_order` backends: serial, sapphire
- `nk_bf16_order` backends: serial
- `nk_e4m3_order` backends: serial
- `nk_e5m2_order` backends: serial
- `nk_e2m3_order` backends: serial
- `nk_e3m2_order` backends: serial

Uses `mask = -sign; ordered = value ^ mask` — the constant offset cancels in subtraction. Returns negative if a < b, 0 if equal, positive if a > b.

- `a` (in): First operand.
- `b` (in): Second operand.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: std::strong_order, Rust total_cmp

Notes: NaN values are ordered at the extremes per IEEE 754 totalOrder (negative NaN < all finite < positive NaN). Callers requiring NaN-exclusion semantics must filter NaN before calling.

## nk_f32_sin_serial

Approximates the sine of an angle in radians within __3-ULP__ error on [-2π, 2π].

```c
nk_f32_t nk_f32_sin_serial(nk_f32_t angle_radians);
```

- `nk_f32_sin` backends: serial

Based on `xfastsinf_u3500` in the SLEEF library.

- `angle_radians` (in): The input angle in radians.

## nk_f32_cos_serial

Approximates the cosine of an angle in radians within __3-ULP__ error on [-2π, 2π].

```c
nk_f32_t nk_f32_cos_serial(nk_f32_t angle_radians);
```

- `nk_f32_cos` backends: serial

Based on `xfastcosf_u3500` in the SLEEF library.

- `angle_radians` (in): The input angle in radians.

## nk_*_atan_serial

Computes the arc-tangent of a value with __0-ULP__ error bound.

```c
nk_f32_t nk_f32_atan_serial(nk_f32_t input);
```

- `nk_f32_atan_serial`: input `nk_f32_t`
- `nk_f64_atan_serial`: input `nk_f64_t`

- `nk_f32_atan` backends: serial
- `nk_f64_atan` backends: serial

Based on `xatanf` in the SLEEF library.

Based on `xatan` in the SLEEF library.

- `input` (in): The input value.

## nk_*_atan2_serial

Computes the arc-tangent of (y/x) with __0-ULP__ error bound.

```c
nk_f32_t nk_f32_atan2_serial(nk_f32_t y_input, nk_f32_t x_input);
```

- `nk_f32_atan2_serial`: y_input, x_input `nk_f32_t`
- `nk_f64_atan2_serial`: y_input, x_input `nk_f64_t`

- `nk_f32_atan2` backends: serial
- `nk_f64_atan2` backends: serial

Based on `xatan2f` in the SLEEF library.

Based on `xatan2` in the SLEEF library.

- `y_input` (in): The input sine value.
- `x_input` (in): The input cosine value.

## nk_f64_sin_serial

Computes the sine of the given angle in radians with __0-ULP__ error bound in [-2π, 2π].

```c
nk_f64_t nk_f64_sin_serial(nk_f64_t angle_radians);
```

- `nk_f64_sin` backends: serial

Based on `xsin` in the SLEEF library.

- `angle_radians` (in): The input angle in radians.

## nk_f64_cos_serial

Computes the cosine of the given angle in radians with __0-ULP__ error bound in [-2π, 2π].

```c
nk_f64_t nk_f64_cos_serial(nk_f64_t angle_radians);
```

- `nk_f64_cos` backends: serial

Based on `xcos` in the SLEEF library.

- `angle_radians` (in): The input angle in radians.

## nk_*_find_kernel

Finds the scalar kernel of `kind` for `dtype`, from the best of `capabilities`.

```c
nk_status_t nk_scalar_find_kernel(
    nk_kernel_kind_t kind,
    nk_dtype_t dtype,
    nk_capability_t capabilities,
    nk_kernel_punned_t *kernel,
    nk_capability_t *capability);
```

- `nk_scalar_find_kernel`
- `nk_cast_find_kernel`

- `kind` (in): The verb whose kernel to find.
- `dtype` (in): The input type the kernel reads.
- `capabilities` (in): The mask to pick the best capability from.
- `kernel` (out): The kernel, or null when no capability in `capabilities` has it.
- `capability` (out): The capability the kernel belongs to, or zero.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k)
- [`nk_missing_library_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_library_k) Header-only.

## nk_cast_best

Elementwise type-casting for arrays of entries, plain or block-scaled.

```c
nk_status_t nk_cast_best(
    void const *from,
    nk_dtype_t from_dtype,
    void *to,
    nk_dtype_t to_dtype,
    nk_size_t count,
    nk_capability_t capabilities,
    nk_stream_t stream);
```

- `nk_cast` backends: serial, neon, haswell, skylake, icelake, sapphire, rvv, powervsx, v128relaxed, cuda, ampere, ada, rocm, metal

A block-scaled dtype is one of the composites, like [`nk_mxfp8e4m3_k`](#nk_mxfp8e4m3_k) or [`nk_nvfp4_k`](#nk_nvfp4_k), whose element dtype, scale dtype and block size [`nk_block_scaled_format_of_dtype`](#nk_block_scaled_format_of_dtype) derives:

| Source | Destination | Action |
|:-|:-|:-|
| plain | plain | elementwise conversion |
| plain | block-scaled | encodes: per-block amax, derived scale, quantization |
| block-scaled | plain | decodes: reads the scale, dequantizes to the plain dtype |
| block-scaled | block-scaled | transcodes: decode → encode |

Codes and scales are contiguous runs. A destination NVFP4 `tensor_scale` holding a non-zero value is applied as is; holding zero, it receives the tensor scale that the kernel derives from the source data it encodes.

- `from` (in): The `count` source elements, or an `nk_<format>_cref_t` if block-scaled.
- `from_dtype` (in): The type of elements in the immutable source array.
- `to` (out): The `count` target elements, or an `nk_<format>_ref_t` if block-scaled.
- `to_dtype` (in): The type of elements in the mutable target array.
- `count` (in): Logical elements, a multiple of both block sizes if either is block-scaled.
- `capabilities` (in): One device's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports.
- `stream` (in): Null on the CPU, or the GPU stream of that device to queue on.

Statuses: - [`nk_missing_kernel_k`](https://www.unum.cloud/docs/numkong/c/quickstart.md#nk_missing_kernel_k) No capability in `capabilities` has it.

## nk_f16_to_f32_best

Scalar conversion from `f16` to `f32`, covering every IEEE 754 edge case.

```c
void nk_f16_to_f32_best(
    nk_f16_t const *src,
    nk_f32_t *dest,
    nk_capability_t capabilities);
```

- `nk_f16_to_f32` backends: serial, neon, haswell, sapphire, powervsx

| Input | F16 Hex | F32 Hex | Description |
|:-|:-|:-|:-|
| +0 | 0x0000 | 0x00000000 | Positive zero |
| -0 | 0x8000 | 0x80000000 | Negative zero |
| +inf | 0x7C00 | 0x7F800000 | Positive infinity |
| -inf | 0xFC00 | 0xFF800000 | Negative infinity |
| NaN | 0x7E00 | 0x7FC00000 | Quiet NaN, payload preserved |
| Min normal | 0x0400 | 0x38800000 | 2⁻¹⁴ |
| Max normal | 0x7BFF | 0x477FE000 | 65504 |
| Min denorm | 0x0001 | 0x33800000 | 2⁻²⁴ |
| Max denorm | 0x03FF | 0x387FC000 | 2⁻¹⁴ - 2⁻²⁴ |

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: Stack Overflow on half-precision conversion: [https://stackoverflow.com/a/60047308](https://stackoverflow.com/a/60047308)

See also: Half-float conversion gist: [https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233](https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233)

See also: Libcanard float16 codec: [https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834](https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834)

## nk_bf16_to_f32_best

Scalar conversion from `bf16` to `f32`.

```c
void nk_bf16_to_f32_best(
    nk_bf16_t const *src,
    nk_f32_t *dest,
    nk_capability_t capabilities);
```

- `nk_bf16_to_f32` backends: serial

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: Stack Overflow on f32 and bf16 conversion: [https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307](https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307)

See also: Bfloat16 on Cloud TPUs: [https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus](https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus)

## nk_*_to_f32_best

Scalar conversion from FP8 `e4m3` to `f32`.

```c
void nk_e4m3_to_f32_best(
    nk_e4m3_t const *src,
    nk_f32_t *dest,
    nk_capability_t capabilities);
```

- `nk_e4m3_to_f32_best`: src `nk_e4m3_t`
- `nk_e5m2_to_f32_best`: src `nk_e5m2_t`

- `nk_e4m3_to_f32` backends: serial
- `nk_e5m2_to_f32` backends: serial

E4M3 has 1 sign bit, 4 exponent bits biased by 7, and 3 mantissa bits. It spans [-448, +448] with no infinities and only two NaN encodings, 0x7F and 0xFF. Subnormals decode to the value (-1)ˢ × mantissa × 2⁻⁹, which is mantissa / 512.

| Input | E4M3 Hex | F32 Hex | Description |
|:-|:-|:-|:-|
| +0 | 0x00 | 0x00000000 | Positive zero |
| -0 | 0x80 | 0x80000000 | Negative zero |
| +NaN | 0x7F | 0x7FC00000 | Quiet NaN, exp = 15, mant ≠ 0 |
| -NaN | 0xFF | 0xFFC00000 | Quiet NaN, signed |
| +448 (max) | 0x7E | 0x43E00000 | Max normal = 448 |
| -448 | 0xFE | 0xC3E00000 | Min normal = -448 |
| 1.0 | 0x38 | 0x3F800000 | Normal, exp = 7, mant = 0 |
| Min denorm | 0x01 | 0x3B000000 | 1/512 = 2⁻⁹ |
| Max denorm | 0x07 | 0x3BE00000 | 7/512 = 7 × 2⁻⁹ |

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: FP8 Formats for Deep Learning: [https://arxiv.org/pdf/2209.05433](https://arxiv.org/pdf/2209.05433)

See also: OCP 8-bit Floating Point Specification: [https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1)

See also: ONNX Float8 types: [https://onnx.ai/onnx/technical/float8.html](https://onnx.ai/onnx/technical/float8.html)

## nk_*_to_f32_best

Scalar conversion from FP6 `e2m3` to `f32`.

```c
void nk_e2m3_to_f32_best(
    nk_e2m3_t const *src,
    nk_f32_t *dest,
    nk_capability_t capabilities);
```

- `nk_e2m3_to_f32_best`: src `nk_e2m3_t`
- `nk_e3m2_to_f32_best`: src `nk_e3m2_t`

- `nk_e2m3_to_f32` backends: serial
- `nk_e3m2_to_f32` backends: serial

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

## nk_f32_to_f16_best

Scalar conversion from `f32` to `f16`, rounding to nearest, for all IEEE 754 inputs.

```c
void nk_f32_to_f16_best(
    nk_f32_t const *src,
    nk_f16_t *dest,
    nk_capability_t capabilities);
```

- `nk_f32_to_f16` backends: serial, neon, haswell, sapphire, powervsx

| Input | F32 Hex | F16 Hex | Description |
|:-|:-|:-|:-|
| +0 | 0x00000000 | 0x0000 | Positive zero |
| -0 | 0x80000000 | 0x8000 | Negative zero |
| +inf | 0x7F800000 | 0x7C00 | Positive infinity |
| -inf | 0xFF800000 | 0xFC00 | Negative infinity |
| NaN | 0x7FC00000 | 0x7E00 | Quiet NaN, payload truncated |
| 1.0 | 0x3F800000 | 0x3C00 | Normal number |
| 65504 | 0x477FE000 | 0x7BFF | Max f16 normal |
| 65520+ | >0x477FE000 | 0x7C00 | Overflow → infinity |
| 2⁻¹⁴ | 0x38800000 | 0x0400 | Min f16 normal |
| 2⁻²⁴ | 0x33800000 | 0x0001 | Min f16 denormal |
| <2⁻²⁵ | <0x33000000 | 0x0000 | Underflow → zero |

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: Stack Overflow on half-precision conversion: [https://stackoverflow.com/a/60047308](https://stackoverflow.com/a/60047308)

See also: Half-float conversion gist: [https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233](https://gist.github.com/milhidaka/95863906fe828198f47991c813dbe233)

See also: Libcanard float16 codec: [https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834](https://github.com/OpenCyphal/libcanard/blob/636795f4bc395f56af8d2c61d3757b5e762bb9e5/canard.c#L811-L834)

## nk_f32_to_bf16_best

Scalar conversion from `f32` to `bf16`.

```c
void nk_f32_to_bf16_best(
    nk_f32_t const *src,
    nk_bf16_t *dest,
    nk_capability_t capabilities);
```

- `nk_f32_to_bf16` backends: serial

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: Stack Overflow on f32 and bf16 conversion: [https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307](https://stackoverflow.com/questions/55253233/convert-fp32-to-bfloat16-in-c/55254307#55254307)

See also: Bfloat16 on Cloud TPUs: [https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus](https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus)

## nk_f32_to_*_best

Scalar conversion from `f32` to FP8 `e4m3`, rounding to nearest even per IEEE 754 and the OCP FP8 specification.

```c
void nk_f32_to_e4m3_best(
    nk_f32_t const *src,
    nk_e4m3_t *dest,
    nk_capability_t capabilities);
```

- `nk_f32_to_e4m3_best`: dest `nk_e4m3_t`
- `nk_f32_to_e5m2_best`: dest `nk_e5m2_t`

- `nk_f32_to_e4m3` backends: serial
- `nk_f32_to_e5m2` backends: serial

E4M3 has 1 sign bit, 4 exponent bits biased by 7, and 3 mantissa bits. It spans [-448, +448] with no infinities and only two NaN encodings. Values with |x| < 2⁻⁶ are encoded as subnormals.

| Input | F32 Hex | E4M3 Hex | Description |
|:-|:-|:-|:-|
| +0 | 0x00000000 | 0x00 | Positive zero |
| -0 | 0x80000000 | 0x80 | Negative zero |
| +inf | 0x7F800000 | 0x7E | Saturates to max, +448 |
| -inf | 0xFF800000 | 0xFE | Saturates to min, -448 |
| NaN | 0x7FC00000 | 0x7F | Quiet NaN |
| 1.0 | 0x3F800000 | 0x38 | Normal, exp = 7, mant = 0 |
| 448+ | >0x43E00000 | 0x7E | Overflow → max |
| 2⁻⁶ | 0x3E800000 | 0x08 | Min normal |
| ≤2⁻¹⁰ | ≤0x3A800000 | 0x00 | Underflow → zero, RNE boundary |

E5M2 has 1 sign bit, 5 exponent bits biased by 15, and 2 mantissa bits. Its range is ±57344, and it keeps IEEE 754 infinities and NaNs. Values with |x| < 2⁻¹⁴ are encoded as subnormals.

| Input | F32 Hex | E5M2 Hex | Description |
|:-|:-|:-|:-|
| +0 | 0x00000000 | 0x00 | Positive zero |
| -0 | 0x80000000 | 0x80 | Negative zero |
| +inf | 0x7F800000 | 0x7C | Positive infinity |
| -inf | 0xFF800000 | 0xFC | Negative infinity |
| NaN | 0x7FC00000 | 0x7D | Quiet NaN |
| 1.0 | 0x3F800000 | 0x3C | Normal, exp = 15, mant = 0 |
| 57344+ | >0x47600000 | 0x7C | Overflow → infinity |
| 2⁻¹⁴ | 0x38800000 | 0x04 | Min normal |
| ≤2⁻¹⁷ | ≤0x37000000 | 0x00 | Underflow → zero, RNE boundary |

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

See also: FP8 Formats for Deep Learning: [https://arxiv.org/pdf/2209.05433](https://arxiv.org/pdf/2209.05433)

See also: OCP 8-bit Floating Point Specification: [https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1](https://www.opencompute.org/documents/ocp-8-bit-floating-point-specification-ofp8-revision-1-0-2023-12-01-pdf-1)

See also: ONNX Float8 types: [https://onnx.ai/onnx/technical/float8.html](https://onnx.ai/onnx/technical/float8.html)

## nk_f32_to_*_best

Scalar conversion from `f32` to FP6 `e2m3`, rounding to nearest even per IEEE 754.

```c
void nk_f32_to_e2m3_best(
    nk_f32_t const *src,
    nk_e2m3_t *dest,
    nk_capability_t capabilities);
```

- `nk_f32_to_e2m3_best`: dest `nk_e2m3_t`
- `nk_f32_to_e3m2_best`: dest `nk_e3m2_t`

- `nk_f32_to_e2m3` backends: serial
- `nk_f32_to_e3m2` backends: serial

E2M3FN has 1 sign bit, 2 exponent bits biased by 1, and 3 mantissa bits. It spans [-7.5, +7.5] with no infinities or NaNs, and saturates to the maximum on overflow. Values with |x| < 0.5 are encoded as subnormals.

E3M2FN has 1 sign bit, 3 exponent bits biased by 3, and 2 mantissa bits. It spans [-28, +28] with no infinities or NaNs, and saturates to the maximum on overflow. Values with |x| < 0.25 are encoded as subnormals.

- `src` (in): The scalar to convert.
- `dest` (out): Receives the converted scalar.
- `capabilities` (in): The CPU's capabilities, like [`nk_cpu_capabilities_enabled`](https://www.unum.cloud/docs/numkong/c/runtime-dispatch.md#nk_cpu_capabilities_enabled) reports; serial always stays.

## nk_e2m1x2_to_f32x2_serial

Unpacks a byte of two E2M1 nibbles into two f32 values: the high nibble lands in `dest[0]` and the low nibble in `dest[1]`, as in `[nk_i4x2_t](#nk_i4x2_t)` and `[nk_u4x2_t](#nk_u4x2_t)`.

```c
void nk_e2m1x2_to_f32x2_serial(nk_e2m1x2_t const *src, nk_f32_t *dest);
```

- `nk_e2m1x2_to_f32x2` backends: serial

## nk_f32x2_to_e2m1x2_serial

Packs two f32 values into one byte of two E2M1 nibbles: `src[0]` becomes the high nibble and `src[1]` the low one, as in `[nk_i4x2_t](#nk_i4x2_t)` and `[nk_u4x2_t](#nk_u4x2_t)`.

```c
void nk_f32x2_to_e2m1x2_serial(nk_f32_t const *src, nk_e2m1x2_t *dest);
```

- `nk_f32x2_to_e2m1x2` backends: serial

## nk_ue8m0_to_f32_serial

Converts a UE8M0 power-of-two scale byte, as used by OCP MX, to f32.

```c
void nk_ue8m0_to_f32_serial(nk_ue8m0_t const *src, nk_f32_t *dest);
```

- `nk_ue8m0_to_f32` backends: serial

A zero byte decodes to 0, 0xFF to NaN as the block-NaN sentinel, and any other v to 2^(v - 127).

## nk_f32_to_ue8m0_serial

Converts an f32 magnitude to a UE8M0 power-of-two scale byte.

```c
void nk_f32_to_ue8m0_serial(nk_f32_t const *src, nk_ue8m0_t *dest);
```

- `nk_f32_to_ue8m0` backends: serial

Rounds to the nearest power of two, ties to even in log2 space, per the OCP MX, NVIDIA and AMD convention. Plain ceil or floor biases the block's dynamic range, as floor underestimates it, so the split sits at the geometric midpoint √2 × 2^e. NaN maps to 0xFF as the block-NaN sentinel, zero and subnormals map to 0x00, and overflow saturates to 0xFE.

## nk_ue4m3_to_f32_serial

Converts a UE4M3 byte, the NVFP4 scale that is an E4M3 with its sign bit forced to 0, to f32.

```c
void nk_ue4m3_to_f32_serial(nk_ue4m3_t const *src, nk_f32_t *dest);
```

- `nk_ue4m3_to_f32` backends: serial

## nk_f32_to_ue4m3_serial

Converts an f32 magnitude to a UE4M3 NVFP4 scale byte.

```c
void nk_f32_to_ue4m3_serial(nk_f32_t const *src, nk_ue4m3_t *dest);
```

- `nk_f32_to_ue4m3` backends: serial

Takes the absolute value first, as the sign bit is not representable, then rounds to nearest even like the underlying E4M3 encoder, matching the NVFP4 and OCP scale convention. NaN maps to the E4M3 NaN code 0x7F.

## nk_nvfp4_to_f32x16_serial

Decode one NVFP4 block (16 elements) to f32.

```c
void nk_nvfp4_to_f32x16_serial(
    nk_nvfp4_t const *src,
    nk_f32_t tensor_scale,
    nk_f32_t *dest);
```

- `nk_nvfp4_to_f32x16` backends: serial

`tensor_scale` is the per-tensor multiplier.

## nk_f32x16_to_nvfp4_serial

Encode 16 f32 values into one NVFP4 block, deriving a UE4M3 scale via per-block amax.

```c
void nk_f32x16_to_nvfp4_serial(
    nk_f32_t const *src,
    nk_f32_t tensor_scale,
    nk_nvfp4_t *dest);
```

- `nk_f32x16_to_nvfp4` backends: serial

## nk_block_scaled_elements_size

Number of element storage bytes needed for `count` logical elements of `format`.

```c
nk_size_t nk_block_scaled_elements_size(
    nk_size_t count,
    nk_block_scaled_format_t format);
```

## nk_block_scaled_scales_size

Number of scale storage bytes needed for `count` logical elements of `format`.

```c
nk_size_t nk_block_scaled_scales_size(
    nk_size_t count,
    nk_block_scaled_format_t format);
```

## nk_nvfp4

`{[nk_e2m1_k](#nk_e2m1_k), [nk_ue4m3_k](#nk_ue4m3_k), [nk_f32_k](#nk_f32_k), 16, 8}` — NVIDIA NVFP4 (Blackwell-native).

```c
nk_block_scaled_format_t nk_nvfp4(void);
```

## nk_mxfp4

`{[nk_e2m1_k](#nk_e2m1_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 16}` — OCP MXFP4.

```c
nk_block_scaled_format_t nk_mxfp4(void);
```

## nk_mxfp6e2m3

`{[nk_e2m3_k](#nk_e2m3_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 32}` — OCP MXFP6 (E2M3 variant).

```c
nk_block_scaled_format_t nk_mxfp6e2m3(void);
```

## nk_mxfp6e3m2

`{[nk_e3m2_k](#nk_e3m2_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 32}` — OCP MXFP6 (E3M2 variant).

```c
nk_block_scaled_format_t nk_mxfp6e3m2(void);
```

## nk_mxfp8e4m3

`{[nk_e4m3_k](#nk_e4m3_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 32}` — OCP MXFP8 (E4M3 variant).

```c
nk_block_scaled_format_t nk_mxfp8e4m3(void);
```

## nk_mxfp8e5m2

`{[nk_e5m2_k](#nk_e5m2_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 32}` — OCP MXFP8 (E5M2 variant).

```c
nk_block_scaled_format_t nk_mxfp8e5m2(void);
```

## nk_mxint8

`{[nk_i8_k](#nk_i8_k), [nk_ue8m0_k](#nk_ue8m0_k), unknown, 32, 32}` — OCP MXINT8.

```c
nk_block_scaled_format_t nk_mxint8(void);
```

## nk_plain

`{element_dtype, unknown, unknown, 0, 0}` — plain scalar buffer of `element_dtype`.

```c
nk_block_scaled_format_t nk_plain(nk_dtype_t element_dtype);
```

## nk_block_scaled_format_of_dtype

Build a block-scaled format descriptor from a composite `dtype` enum value.

```c
nk_block_scaled_format_t nk_block_scaled_format_of_dtype(
    nk_dtype_t dtype);
```

Returns `nk_plain(dtype)` when `dtype` is not a composite.

## nk_dtype_is_block_scaled

True when `dtype` encodes a composite block-scaled format.

```c
int nk_dtype_is_block_scaled(nk_dtype_t dtype);
```

## nk_dtype_element

Extracts the element dtype from a composite; returns `dtype` unchanged for plain inputs.

```c
nk_dtype_t nk_dtype_element(nk_dtype_t dtype);
```

## nk_dtype_scale

Extracts the scale dtype from a composite; returns [`nk_dtype_unknown_k`](#nk_dtype_unknown_k) for plain inputs.

```c
nk_dtype_t nk_dtype_scale(nk_dtype_t dtype);
```

## nk_dtype_block_size

Block size implied by a composite dtype; 0 for plain inputs.

```c
nk_size_t nk_dtype_block_size(nk_dtype_t dtype);
```

## nk_dtype_family

Classifies the family of the dtype.

```c
nk_dtype_family_t nk_dtype_family(nk_dtype_t dtype);
```

## nk_dtype_bits

Returns the number of bits in a single scalar of a given type.

```c
nk_size_t nk_dtype_bits(nk_dtype_t dtype);
```

## nk_accumulation_error_bound

Returns the rounding error each term may add to a sum kept in `dtype`, relative to the terms' magnitudes: |result − exact| ≤ (roundings + 1) · bound · Σ|terms|.

```c
nk_f64_t nk_accumulation_error_bound(nk_dtype_t dtype);
```

Floats get their unit roundoff with two bits of slack for matrix units that truncate; integer sums are exact. Kernel families derive their bounds from it, like [`nk_dot_error_bound`](https://www.unum.cloud/docs/numkong/c/dot-products.md#nk_dot_error_bound).

## nk_dtype_named

Inverse of `nk_dtype_name` over an explicit-length string; [`nk_dtype_unknown_k`](#nk_dtype_unknown_k) for unrecognized names.

```c
nk_dtype_t nk_dtype_named(char const *name, nk_size_t length);
```

## nk_dimensions_per_value

Returns how many logical dimensions are packed into one storage value.

```c
nk_size_t nk_dimensions_per_value(nk_dtype_t dtype);
```

For sub-byte types multiple dimensions share a single byte container. For byte-or-larger types this is always 1.

## NUMKONG_F64_INF

Infinities as an overflowing product, since C names them portably only in `<math.h>`.

```c
#define NUMKONG_F64_INF ((nk_f64_t)(1e300 * 1e300))
```

## nk_min_of_two

The smaller of two values of any comparable type; each argument is evaluated twice.

```c
#define nk_min_of_two(first, second) ((first) <(second) ? (first) : (second))
```

## nk_max_of_two

The larger of two values of any comparable type; each argument is evaluated twice.

```c
#define nk_max_of_two(first, second) ((first) <(second) ? (second) : (first))
```

