# 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/types.hpp>`

`#include <numkong/cast.hpp>`

## f16c_t

32-bit half-precision complex number, composed of two [f16_t](#f16_t).

```cpp
struct f16c_t
```

Layout: {real: [f16_t](#f16_t), imag: [f16_t](#f16_t)}. Kernel outputs widen to f32c; math runs via f32 upcast.

## bf16c_t

32-bit BFloat16 complex number, composed of two [bf16_t](#bf16_t).

```cpp
struct bf16c_t
```

Layout: {real: [bf16_t](#bf16_t), imag: [bf16_t](#bf16_t)}. Kernel outputs widen to f32c; math runs via f32 upcast.

## e2m3_t

Float6 E2M3FN: 1 sign + 2 exponent, bias=1, + 3 mantissa bits, with 2 bits of padding.

```cpp
struct e2m3_t
```

Range: [-7.5, +7.5], stored byte-aligned as 0b00SEEMMM with the upper 2 bits as padding. Format: OCP Microscaling FN with no Inf/NaN, so all 64 bit patterns are valid numbers. Codes: 48 normal, 14 subnormal with exp=0 and mant!=0, and 2 zeros, ±0. Unit range: only 18 of 64 values, 28.1%, fall in [-1, +1] — 72% of codes represent |x| > 1. Subnormals: span [±0.125, ±0.875] as 0.mmm × 2^(1-bias). Dot products: exact in integers, as every value × 16 is an integer in [-120, +120], fitting i16 products and i32 sums without rounding. Promotion: lossless to E4M3, rebiasing exponents by +6 for normals or normalizing subnormals.

See also: [https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf)

See also: The FP6-LLM paper: [https://arxiv.org/abs/2401.14112](https://arxiv.org/abs/2401.14112)

## e3m2_t

Float6 E3M2FN: 1 sign + 3 exponent, bias=3, + 2 mantissa bits, with 2 bits of padding.

```cpp
struct e3m2_t
```

Range: [-28, +28], stored byte-aligned as 0b00SEEEMM with the upper 2 bits as padding. Format: OCP Microscaling FN with no Inf/NaN, so all 64 bit patterns are valid numbers. Codes: 56 normal, 6 subnormal with exp=0 and mant!=0, and 2 zeros, ±0. Unit range: 26 of 64 values, 40.6%, fall in [-1, +1]. Subnormals: span [±0.0625, ±0.1875] as 0.mm × 2^(1-bias). Dot products: exact in integers, as every value × 4 is an integer in [-28, +28], fitting i16 products and i32 sums without rounding. Promotion: lossless to E5M2, rebiasing exponents by +12 for normals or normalizing subnormals.

See also: [https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf)

See also: The FP6-LLM paper: [https://arxiv.org/abs/2401.14112](https://arxiv.org/abs/2401.14112)

## ue8m0_t

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

```cpp
struct ue8m0_t
```

Encoding: v=0 → zero; v=0xFF → NaN-block sentinel; otherwise 2^(v − 127). Used as the per-block scale byte for MXFP4 / MXFP6 / MXFP8 / MXINT8.

## ue4m3_t

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

```cpp
struct ue4m3_t
```

Range: [0, +448]. Paired with an f32 tensor_scale multiplier in block-scaled NVFP4, block=16.

## f118c_t

Extended-precision complex number using [f118_t](#f118_t) components.

```cpp
struct f118c_t
```

Provides ~91 bits of precision for complex arithmetic, useful for:

- High-precision reference computations in tests
- Complex dot products and correlations
- Avoiding Boost.Multiprecision dependencyFormat: real + i*imag where real and imag are [f118_t](#f118_t) (double-double).

## i8_t

Signed 8-bit integer wrapper.

```cpp
struct i8_t
```

Range: [−128, +127]. Exact integer arithmetic, wrapping on overflow. Dot products accumulated into i32 via VNNI / VPDPBUSD / SDOT.

## u8_t

Unsigned 8-bit integer wrapper.

```cpp
struct u8_t
```

Range: [0, 255]. Exact integer arithmetic, wrapping on overflow. Dot products accumulated into i32 via VNNI / VPDPBUSD / UDOT.

## i32_t

Signed 32-bit integer wrapper.

```cpp
struct i32_t
```

Range: [−2³¹, +2³¹−1]. Exact integer arithmetic, wrapping on overflow.

## u32_t

Unsigned 32-bit integer wrapper.

```cpp
struct u32_t
```

Range: [0, 2³²−1]. Exact integer arithmetic, wrapping on overflow.

## i64_t

Signed 64-bit integer wrapper.

```cpp
struct i64_t
```

Range: [−2⁶³, +2⁶³−1]. Exact integer arithmetic, wrapping on overflow.

## u64_t

Unsigned 64-bit integer wrapper.

```cpp
struct u64_t
```

Range: [0, 2⁶⁴−1]. Exact integer arithmetic, wrapping on overflow.

## i16_t

Signed 16-bit integer wrapper.

```cpp
struct i16_t
```

Range: [−32768, +32767]. Exact integer arithmetic, wrapping on overflow.

## u16_t

Unsigned 16-bit integer wrapper.

```cpp
struct u16_t
```

Range: [0, 65535]. Exact integer arithmetic, wrapping on overflow.

## u1x8_t

Packed 8-bit bit-vector, 8 booleans in one byte.

```cpp
struct u1x8_t
```

Layout: 8 bits packed into one byte, MSB = dimension 0. Used for Hamming distance and Jaccard similarity via popcount.

## i4x2_t

Packed 4-bit signed integer pair, 2 × i4 in one byte.

```cpp
struct i4x2_t
```

Layout: [high nibble : low nibble]. Elements sign-extended to i8 for arithmetic. Range per element: [−8, +7].

## u4x2_t

Packed 4-bit unsigned integer pair, 2 × u4 in one byte.

```cpp
struct u4x2_t
```

Layout: [high nibble : low nibble]. Elements zero-extended to u8 for arithmetic. Range per element: [0, 15].

## nvfp4_t

NVIDIA NVFP4 block value: 16 E2M1 nibbles + 1 UE4M3 scale byte per value.

```cpp
struct nvfp4_t
```

Per-tensor f32 tensor_scale multiplier lives on the enclosing tensor, not per block, and is passed explicitly to `decode_to` and `encode_from`. `sizeof(nvfp4_t) == 9`, and one value represents 16 logical elements — `nk::tensor<nvfp4_t>(n)` allocates ⌈n/16⌉ × 9 bytes.

## mxfp4_t

OCP MXFP4 block value: 32 E2M1 nibbles + 1 UE8M0 pow-2 scale byte per value.

```cpp
struct mxfp4_t
```

## mxfp6e2m3_t

OCP MXFP6 E2M3 block value: 32 E2M3 elements + 1 UE8M0 scale byte per value.

```cpp
struct mxfp6e2m3_t
```

## mxfp6e3m2_t

OCP MXFP6 E3M2 block value: 32 E3M2 elements + 1 UE8M0 scale byte per value.

```cpp
struct mxfp6e3m2_t
```

## mxfp8e4m3_t

OCP MXFP8 E4M3 block value: 32 E4M3 elements + 1 UE8M0 scale byte per value.

```cpp
struct mxfp8e4m3_t
```

## mxfp8e5m2_t

OCP MXFP8 E5M2 block value: 32 E5M2 elements + 1 UE8M0 scale byte per value.

```cpp
struct mxfp8e5m2_t
```

## mxint8_t

OCP MXINT8 block value: 32 signed i8 elements + 1 UE8M0 scale byte per value.

```cpp
struct mxint8_t
```

## numeric_limits_of

What `std::numeric_limits` reports for a real NumKong scalar: every member of the primary template, read off the scalar's own traits.

```cpp
template <typename scalar_type_>
struct numeric_limits_of
```

Complex and packed scalars get none. The decimal bounds take 30103 / 100000 for log10(2), truncating toward zero as the standard rounds them.

- `is_specialized` `bool`
- `is_signed` `bool`
- `is_integer` `bool`
- `is_exact` `bool`
- `has_infinity` `bool`
- `has_quiet_NaN` `bool`
- `has_signaling_NaN` `bool`
- `round_style` `std::float_round_style`
- `is_iec559` `bool`
- `is_bounded` `bool`
- `is_modulo` `bool`
- `digits` `int`
- `digits10` `int`
- `max_digits10` `int`
- `radix` `int`
- `min_exponent` `int`
- `min_exponent10` `int`
- `max_exponent` `int`
- `max_exponent10` `int`
- `traps` `bool`
- `tinyness_before` `bool`

## word_type

Extract the word type from a value type.

```cpp
template <typename value_type_,
          typename = void>
struct word_type
```

For complex types, [f32c_t](#f32c_t), [f64c_t](#f64c_t), and so on, returns the component type, [f32_t](#f32_t), [f64_t](#f64_t). For all other types, returns the type itself.

## bf16_t

16-bit brain floating-point wrapper with f32-compatible exponent range.

```cpp
struct bf16_t
```

Layout: sign(1) + exponent(8) + mantissa(7), bias=127. Same exponent range as f32, ~38 orders of magnitude, but only 7 mantissa bits versus 23. Range: [-3.39e38, +3.39e38], epsilon at 1.0: ~7.81e-3, subnormal min: ~9.18e-41. 32,514 of 65,280 finite values, 49.8%, fall in [-1, +1]. All math runs via f32 upcast/downcast.

## bf16_t

```cpp
constexpr bf16_t() noexcept;
```

```cpp
bf16_t(float v) noexcept;
```

```cpp
bf16_t(double v) noexcept;
```

```cpp
template <std::integral integral_type_>
bf16_t(integral_type_ v) noexcept;
```

## *

```cpp
bool operator==(bf16_t o) const noexcept;
```

- `operator==`: o `bf16_t`
- `operator==`: o `e4m3_t`
- `operator==`: o `e5m2_t`
- `operator==`: o `f16_t`
- `operator==`: o `f32_t`
- `operator==`: o `f32c_t`
- `operator==`: o `f64_t`
- `operator==`: o `f64c_t`

## order

Total ordering via nk_bf16_order_best.

```cpp
int order(bf16_t o) const noexcept;
```

## total_*_cmp

Alias for order(), Rust-style.

```cpp
int total__cmp(bf16_t o) const noexcept;
```

- `total_cmp`: o `bf16_t`
- `total_cmp`: o `e4m3_t`
- `total_cmp`: o `e5m2_t`
- `total_cmp`: o `f118_t`
- `total_cmp`: o `f16_t`
- `total_cmp`: o `f32_t`
- `total_cmp`: o `f64_t`

## saturating_*_add

Saturating addition: clamps to finite range on overflow.

```cpp
bf16_t saturating__add(bf16_t o) const noexcept;
```

- `saturating_add`: o `bf16_t`
- `saturating_add`: o `e4m3_t`
- `saturating_add`: o `e5m2_t`
- `saturating_add`: o `f16_t`
- `saturating_add`: o `f32_t`
- `saturating_add`: o `f64_t`

## saturating_*_sub

Saturating subtraction: clamps to finite range on overflow.

```cpp
bf16_t saturating__sub(bf16_t o) const noexcept;
```

- `saturating_sub`: o `bf16_t`
- `saturating_sub`: o `e4m3_t`
- `saturating_sub`: o `e5m2_t`
- `saturating_sub`: o `f16_t`
- `saturating_sub`: o `f32_t`
- `saturating_sub`: o `f64_t`

## e2m1x2_t

Packed 4-bit E2M1 micro-float pair, 2 × e2m1 in one byte.

```cpp
struct e2m1x2_t
```

Layout: [high nibble : low nibble]. Each nibble: sign(1) + exponent(2) + mantissa(1), bias=1. Eight magnitudes {0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0} × 2 signs. Range: ±6.0, no Inf/NaN. Element of MXFP4, block=32, UE8M0 scales, and NVFP4, block=16, UE4M3 scales.

## nibble_to_f32

Decodes one nibble, low 4 bits, of sign, 2 exponent and 1 mantissa bits.

```cpp
float nibble_to_f32(nk_u8_t nibble) noexcept;
```

## e2m1x2_t

```cpp
constexpr e2m1x2_t() noexcept;
```

```cpp
constexpr e2m1x2_t(raw_t v) noexcept;
```

## first

The first element, stored in the high nibble.

```cpp
float first() const noexcept;
```

## second

The second element, stored in the low nibble.

```cpp
float second() const noexcept;
```

## e4m3_t

FP8 E4M3 wrapper: 8-bit float with 4-bit exponent, 3-bit mantissa.

```cpp
struct e4m3_t
```

Layout: sign(1) + exponent(4) + mantissa(3), bias=7. Range: ±448, epsilon at 1.0: 0.125, subnormal min: ~1.95e-3. No infinity representation; only 2 NaN codes, 0x7F and 0xFF. 114 of 254 finite values, 44.9%, fall in [-1, +1]. Dot products can be computed exactly via integer decomposition: mantissa products (8+m_a)*(8+m_b) are accumulated into 29 bins indexed by exponent sum (e_a+e_b). All math runs via f32 upcast/downcast.

## e4m3_t

```cpp
constexpr e4m3_t() noexcept;
```

```cpp
e4m3_t(float v) noexcept;
```

```cpp
e4m3_t(double v) noexcept;
```

```cpp
template <std::integral integral_type_>
e4m3_t(integral_type_ v) noexcept;
```

## order

Total ordering via nk_e4m3_order_best.

```cpp
int order(e4m3_t o) const noexcept;
```

## e5m2_t

FP8 E5M2 wrapper: 8-bit float with 5-bit exponent, 2-bit mantissa.

```cpp
struct e5m2_t
```

Layout: sign(1) + exponent(5) + mantissa(2), bias=15. Same exponent range as f16, but only 2 mantissa bits. Range: ±57344, epsilon at 1.0: 0.25, subnormal min: ~1.53e-5. Supports infinity and NaN, IEEE-like, exp=31. 122 of 248 finite values, 49.2%, fall in [-1, +1]. The huge dynamic range, ratio ~3.7e9, makes f32 accumulation vulnerable to catastrophic cancellation when large and small products coexist. Dot products can be computed exactly via integer decomposition: mantissa products (4+m_a)*(4+m_b) accumulate into 63 bins indexed by exponent sum (e_a+e_b). All math runs via f32 upcast/downcast.

## e5m2_t

```cpp
constexpr e5m2_t() noexcept;
```

```cpp
e5m2_t(float v) noexcept;
```

```cpp
e5m2_t(double v) noexcept;
```

```cpp
template <std::integral integral_type_>
e5m2_t(integral_type_ v) noexcept;
```

## order

Total ordering via nk_e5m2_order_best.

```cpp
int order(e5m2_t o) const noexcept;
```

## f118_t

Hardware-friendly __"double-double"__ arithmetic with ~106-bit mantissa.

```cpp
struct f118_t
```

Uses Knuth two-sum + FMA for error-free transformations. Provides ~106 bits of mantissa precision using two doubles, at roughly 11x the cost of native `double` arithmetic, being roughly 8x more efficient than Boost.Multiprecision types.

Speed comparison, relative to double:

| Type | Speed | Mantissa | Notes |
|:-|:-|:-|:-|
| double | 1.0x | 53-bit | Hardware |
| long double | 1.5x | 64-bit | x87 hardware |
| f118_t | 11x | ~106-bit | Software, FMA-based |
| __float128 | 88x | 113-bit | libquadmath |
| boost::float128 | 91x | 113-bit | Wrapper around __float128 |
| boost::cpp_bin_float_quad | 200x | 113-bit | Pure C++ (slowest!) |
| boost::cpp_bin_float_50 | 237x | ~166-bit | 50 decimal digits |

Measured precision vs __float128, 1M random samples, full double-double input:

| Operation | Max Rel Err | Precision | Notes |
|:-|:-|:-|:-|
| +, - | ~2e-28 | ~91 bits | Two-Sum algorithm |
| * | 2.5e-32 | ~104 bits | FMA-based Two-Prod |
| / | 2.4e-32 | ~105 bits | 3-iteration Newton-Raphson |
| sqrt | 6.8e-32 | ~103 bits | Newton-Raphson refinement |
| cbrt | 2.4e-31 | ~101 bits | Newton-Raphson refinement |
| exp | 2.2e-31 | ~101 bits | Argument reduction + Taylor |
| log | 2.3e-32 | ~105 bits | Newton-Raphson refinement |
| sin | 6.8e-32 | ~103 bits | \|x\|<10, quad-double π/2 + Taylor |
| cos | 7.8e-32 | ~103 bits | \|x\|<10, quad-double π/2 + Taylor |
| tan | 9.4e-32 | ~103 bits | sin/cos ratio |
| sinh | 1.4e-31 | ~102 bits | Taylor for \|x\|<1, optimized exp |
| cosh | 1.5e-31 | ~102 bits | Via exp() |
| tanh | 1.0e-31 | ~102 bits | sinh/cosh ratio |
| pow | 1.0e-30 | ~99 bits | Via exp(y·log(x)) |

## f118_t

Default constructor, initializes to zero.

```cpp
constexpr f118_t() noexcept;
```

```cpp
constexpr f118_t(double h, double l) noexcept;
```

Construct from high and low double components.

```cpp
constexpr f118_t(double v) noexcept;
```

Construct from single double.

```cpp
constexpr f118_t(int v) noexcept;
```

Construct from int, resolving ambiguity with the double,double constructor.

```cpp
constexpr f118_t(unsigned int v) noexcept;
```

Construct from unsigned int.

```cpp
constexpr f118_t(std::int64_t v) noexcept;
```

Construct from int64_t, capturing precision beyond double's 53-bit mantissa.

```cpp
constexpr f118_t(std::uint64_t v) noexcept;
```

Construct from uint64_t, capturing precision beyond double's 53-bit mantissa.

## operator+

Addition with ~91 bits precision, max rel err 2.3e-28 vs __float128.

```cpp
f118_t operator+(f118_t const & o) const noexcept;
```

## operator+=

In-place addition.

```cpp
f118_t & operator+=(f118_t const & o) noexcept;
```

## operator-=

In-place subtraction.

```cpp
f118_t & operator-=(f118_t const & o) noexcept;
```

## operator*=

In-place multiplication.

```cpp
f118_t & operator*=(f118_t const & o) noexcept;
```

## operator/=

In-place division.

```cpp
f118_t & operator/=(f118_t const & o) noexcept;
```

## operator-

Subtraction with ~93 bits precision, max rel err 7.4e-29 vs __float128.

```cpp
f118_t operator-(f118_t const & o) const noexcept;
```

```cpp
f118_t operator-() const noexcept;
```

Unary minus.

## operator*

Multiplication with ~104 bits precision, max rel err 2.5e-32 vs __float128, ~105 bits vs Boost.

```cpp
f118_t operator*(f118_t const & o) const noexcept;
```

## operator/

Division with ~105 bits precision, max rel err 2.4e-32 vs __float128, ~106 bits vs Boost.

```cpp
f118_t operator/(f118_t const & o) const noexcept;
```

## saturating_add

Saturating addition - clamps to max finite value instead of infinity.

```cpp
f118_t saturating_add(f118_t o) const noexcept;
```

## saturating_sub

Saturating subtraction - clamps to max finite value instead of infinity.

```cpp
f118_t saturating_sub(f118_t o) const noexcept;
```

## saturating_mul

Saturating multiplication - forwards to operator*, no saturation semantics for double-double.

```cpp
f118_t saturating_mul(f118_t o) const noexcept;
```

## operator==

Exact equality, both high_ and low_ must match.

```cpp
bool operator==(f118_t const & o) const noexcept;
```

## operator!=

Exact inequality.

```cpp
bool operator!=(f118_t const & o) const noexcept;
```

## operator<

Less-than comparison, lexicographic on high_, then low_.

```cpp
bool operator<(f118_t const & o) const noexcept;
```

## operator>

Greater-than comparison.

```cpp
bool operator>(f118_t const & o) const noexcept;
```

## operator<=

Less-than-or-equal comparison.

```cpp
bool operator<=(f118_t const & o) const noexcept;
```

## operator>=

Greater-than-or-equal comparison.

```cpp
bool operator>=(f118_t const & o) const noexcept;
```

## operator double

Convert to double, losing ~53 bits of precision.

```cpp
constexpr operator double() const noexcept;
```

## *

Convert to any numeric type.

```cpp
template <typename target_type_>
target_type_ to() const noexcept;
```

## sqrt

Square root with ~103 bits precision, max rel err 6.8e-32 vs __float128, roughly ~103 bits vs Boost.

```cpp
f118_t sqrt() const noexcept;
```

## rsqrt

Reciprocal square root, 1/sqrt.

```cpp
f118_t rsqrt() const noexcept;
```

## exp

Exponential with ~101 bits precision, max rel err 2.2e-31 vs __float128, roughly ~102 bits vs Boost.

```cpp
f118_t exp() const noexcept;
```

## log

Natural logarithm with ~105 bits precision, max rel err 2.2e-32 vs __float128, ~105 bits vs Boost.

```cpp
f118_t log() const noexcept;
```

## sin

Sine with ~103 bits precision (max rel err: 6.8e-32 vs __float128) for |x| < 10.

```cpp
f118_t sin() const noexcept;
```

Notes: Quad-double π/2 with staged error-free reduction; precision degrades for |x| > 1000.

## cos

Cosine with ~103 bits precision (max rel err: 7.8e-32 vs __float128) for |x| < 10.

```cpp
f118_t cos() const noexcept;
```

Notes: Quad-double π/2 with staged error-free reduction; precision degrades for |x| > 1000.

## abs

Absolute value.

```cpp
f118_t abs() const noexcept;
```

## is_nan

Returns true if either component is NaN.

```cpp
bool is_nan() const noexcept;
```

## is_infinite

Returns true if the value is positive or negative infinity.

```cpp
bool is_infinite() const noexcept;
```

## is_finite

Returns true if the value is neither infinite nor NaN.

```cpp
bool is_finite() const noexcept;
```

## is_sign_positive

Returns true if the sign bit is positive, +0 included.

```cpp
bool is_sign_positive() const noexcept;
```

## is_sign_negative

Returns true if the sign bit is negative, -0 included.

```cpp
bool is_sign_negative() const noexcept;
```

## floor

Largest integer less than or equal to self.

```cpp
f118_t floor() const noexcept;
```

## ceil

Smallest integer greater than or equal to self.

```cpp
f118_t ceil() const noexcept;
```

## round

Nearest integer, rounding half away from zero.

```cpp
f118_t round() const noexcept;
```

## trunc

Integer part, truncated toward zero.

```cpp
f118_t trunc() const noexcept;
```

## fract

Fractional part, self - trunc(self).

```cpp
f118_t fract() const noexcept;
```

## min

Returns the minimum of self and other.

```cpp
f118_t min(f118_t o) const noexcept;
```

## max

Returns the maximum of self and other.

```cpp
f118_t max(f118_t o) const noexcept;
```

## clamp

Clamps self to the range [lower, upper].

```cpp
f118_t clamp(f118_t lower, f118_t upper) const noexcept;
```

## order

Total ordering: -NaN < -Inf < ... < -0 < +0 < ... < +Inf < +NaN.

```cpp
int order(f118_t o) const noexcept;
```

Returns -1, 0, or 1.

## signum

Returns -1, 0, or 1 based on sign.

```cpp
f118_t signum() const noexcept;
```

## copysign

Returns value with magnitude of self and sign of `sign`.

```cpp
f118_t copysign(f118_t sign) const noexcept;
```

## recip

Returns 1 / self.

```cpp
f118_t recip() const noexcept;
```

## powf

xʸ with ~99 bits precision, max rel err 1.0e-30 vs __float128.

```cpp
f118_t powf(f118_t y) const noexcept;
```

## powi

Integer power via binary exponentiation.

```cpp
f118_t powi(int exponent) const noexcept;
```

## cbrt

Cube root with ~101 bits precision, max rel err 2.4e-31 vs __float128.

```cpp
f118_t cbrt() const noexcept;
```

## exp2

2ˣ.

```cpp
f118_t exp2() const noexcept;
```

## log2

Base-2 logarithm.

```cpp
f118_t log2() const noexcept;
```

## log10

Base-10 logarithm.

```cpp
f118_t log10() const noexcept;
```

## exp_m1

eˣ − 1, accurate for small x.

```cpp
f118_t exp_m1() const noexcept;
```

## ln_1p

ln(1 + x), accurate for small x.

```cpp
f118_t ln_1p() const noexcept;
```

## tan

Tangent with ~103 bits precision, max rel err 9.4e-32 vs __float128.

```cpp
f118_t tan() const noexcept;
```

## asin

Arcsine, inverse sine.

```cpp
f118_t asin() const noexcept;
```

## acos

Arccosine, inverse cosine.

```cpp
f118_t acos() const noexcept;
```

## atan

Arctangent, inverse tangent.

```cpp
f118_t atan() const noexcept;
```

## atan2

Four-quadrant arctangent: atan2(y, x) where this = y.

```cpp
f118_t atan2(f118_t x) const noexcept;
```

## sin_cos

Computes both sin(x) and cos(x), returning them in an array.

```cpp
void sin_cos(f118_t & out_sin, f118_t & out_cos) const noexcept;
```

## sinh

Hyperbolic sine with ~102 bits precision, max rel err ~1.4e-31 vs __float128.

```cpp
f118_t sinh() const noexcept;
```

## cosh

Hyperbolic cosine with ~102 bits precision, max rel err 1.5e-31 vs __float128.

```cpp
f118_t cosh() const noexcept;
```

## tanh

Hyperbolic tangent with ~102 bits precision, max rel err ~1e-31 vs __float128.

```cpp
f118_t tanh() const noexcept;
```

## asinh

Inverse hyperbolic sine: ln(x + √(x² + 1)).

```cpp
f118_t asinh() const noexcept;
```

## acosh

Inverse hyperbolic cosine: ln(x + √(x² − 1)).

```cpp
f118_t acosh() const noexcept;
```

## atanh

Inverse hyperbolic tangent: ½ · ln((1+x)/(1−x)).

```cpp
f118_t atanh() const noexcept;
```

## hypot

√(x² + y²) without overflow.

```cpp
f118_t hypot(f118_t y) const noexcept;
```

## fma

Fused multiply-add: self * a + b.

```cpp
f118_t fma(f118_t a, f118_t b) const noexcept;
```

## to_radians

Convert degrees to radians.

```cpp
f118_t to_radians() const noexcept;
```

## to_degrees

Convert radians to degrees.

```cpp
f118_t to_degrees() const noexcept;
```

## f16_t

16-bit half-precision IEEE 754 floating-point wrapper.

```cpp
struct f16_t
```

Layout: sign(1) + exponent(5) + mantissa(10), bias=15. Range: [-65504, +65504], epsilon at 1.0: ~9.77e-4, subnormal min: ~5.96e-8. 30,722 of 63,488 finite values, 48.4%, fall in [-1, +1]. All math runs via f32 upcast/downcast.

## f16_t

```cpp
constexpr f16_t() noexcept;
```

```cpp
f16_t(float v) noexcept;
```

```cpp
f16_t(double v) noexcept;
```

```cpp
template <std::integral integral_type_>
f16_t(integral_type_ v) noexcept;
```

## order

Total ordering via nk_f16_order_best.

```cpp
int order(f16_t o) const noexcept;
```

## f32_t

32-bit single-precision IEEE 754 floating-point wrapper.

```cpp
struct f32_t
```

Layout: sign(1) + exponent(8) + mantissa(23), bias=127. Range: ±3.40×10³⁸, epsilon at 1.0 ≈ 1.19×10⁻⁷, subnormal min ≈ 1.40×10⁻⁴⁵.

Notes: Only bit-manipulation and pure-arithmetic functions are constexpr. STL cmath functions become constexpr in C++26.

## f32_t

```cpp
constexpr f32_t() noexcept;
```

```cpp
constexpr f32_t(float v) noexcept;
```

```cpp
constexpr f32_t(double v) noexcept;
```

```cpp
template <std::integral integral_type_>
constexpr f32_t(integral_type_ v) noexcept;
```

## *

Total ordering: -NaN < -Inf < ... < +Inf < +NaN.

```cpp
int order(f32_t o) const noexcept;
```

- `order`: o `f32_t`
- `order`: o `f64_t`

## saturating_*_mul

Saturating multiplication: clamps to finite range on overflow.

```cpp
f32_t saturating__mul(f32_t o) const noexcept;
```

- `saturating_mul`: o `f32_t`
- `saturating_mul`: o `f64_t`

## f32c_t

64-bit single-precision complex number, composed of two [f32_t](#f32_t).

```cpp
struct f32c_t
```

Layout: {real: [f32_t](#f32_t), imag: [f32_t](#f32_t)}. Uses composition, not inheritance. Supports complex arithmetic, conjugate, norm, abs, arg, and full transcendentals.

## f32c_t

```cpp
constexpr f32c_t() noexcept;
```

```cpp
constexpr f32c_t(f32_t r) noexcept;
```

```cpp
constexpr f32c_t(float r) noexcept;
```

```cpp
constexpr f32c_t(f32_t r, f32_t i) noexcept;
```

```cpp
constexpr f32c_t(float r, float i) noexcept;
```

```cpp
constexpr f32c_t(raw_t r) noexcept;
```

## *

Complex conjugate: `a - bi`.

```cpp
f32c_t conj() const noexcept;
```

## *

Squared magnitude, a² + b².

```cpp
f32_t norm() const noexcept;
```

## *

Magnitude, √(a² + b²).

```cpp
f32_t abs() const noexcept;
```

## *

Phase angle: `atan2(b, a)`.

```cpp
f32_t arg() const noexcept;
```

## *

Complex exponential, e^(a+bi) = e^a(cos(b) + i · sin(b)).

```cpp
f32c_t exp() const noexcept;
```

## *

Complex natural logarithm, ln|z| + i · arg(z).

```cpp
f32c_t log() const noexcept;
```

## *

Complex base-10 logarithm.

```cpp
f32c_t log10() const noexcept;
```

## *

Complex square root, principal value.

```cpp
f32c_t sqrt() const noexcept;
```

## *

Complex power, z^w = e^(w · ln(z)).

```cpp
f32c_t pow(f32c_t w) const noexcept;
```

- `pow`: w `f32c_t`
- `pow`: w `f64c_t`

## *

Real power: `z^x`.

```cpp
f32c_t powf(f32_t x) const noexcept;
```

- `powf`: x `f32_t`
- `powf`: x `f64_t`

## *

Complex sine: sin(z) = (eⁱᶻ − e⁻ⁱᶻ) / 2i.

```cpp
f32c_t sin() const noexcept;
```

## *

Complex cosine: cos(z) = (eⁱᶻ + e⁻ⁱᶻ) / 2.

```cpp
f32c_t cos() const noexcept;
```

## *

Complex tangent: `tan(z) = sin(z) / cos(z)`.

```cpp
f32c_t tan() const noexcept;
```

## *

Complex arcsine, asin(z) = -i · ln(iz + √(1 - z²)).

```cpp
f32c_t asin() const noexcept;
```

## *

Complex arccosine: `acos(z) = π/2 - asin(z)`.

```cpp
f32c_t acos() const noexcept;
```

## *

Complex arctangent, atan(z) = (i/2) · ln((i+z)/(i-z)).

```cpp
f32c_t atan() const noexcept;
```

## *

Complex hyperbolic sine: sinh(z) = (eᶻ − e⁻ᶻ) / 2.

```cpp
f32c_t sinh() const noexcept;
```

## *

Complex hyperbolic cosine: cosh(z) = (eᶻ + e⁻ᶻ) / 2.

```cpp
f32c_t cosh() const noexcept;
```

## *

Complex hyperbolic tangent: `tanh(z) = sinh(z) / cosh(z)`.

```cpp
f32c_t tanh() const noexcept;
```

## *

Complex inverse hyperbolic sine, asinh(z) = ln(z + √(z² + 1)).

```cpp
f32c_t asinh() const noexcept;
```

## *

Complex inverse hyperbolic cosine, acosh(z) = ln(z + √(z² - 1)).

```cpp
f32c_t acosh() const noexcept;
```

## *

Complex inverse hyperbolic tangent, atanh(z) = (1/2) · ln((1+z)/(1-z)).

```cpp
f32c_t atanh() const noexcept;
```

## *

Reciprocal: `1 / z`.

```cpp
f32c_t recip() const noexcept;
```

## f64_t

64-bit double-precision IEEE 754 floating-point wrapper.

```cpp
struct f64_t
```

Layout: sign(1) + exponent(11) + mantissa(52), bias=1023. Range: ±1.80×10³⁰⁸, epsilon at 1.0 ≈ 2.22×10⁻¹⁶, subnormal min ≈ 4.94×10⁻³²⁴.

Notes: Only bit-manipulation and pure-arithmetic functions are constexpr. STL cmath functions become constexpr in C++26.

## f64_t

Upcast from any nk numeric wrapper with `operator float()`, such as [f16_t](#f16_t), [bf16_t](#bf16_t), [e4m3_t](#e4m3_t), [i8_t](#i8_t).

```cpp
constexpr f64_t() noexcept;
```

```cpp
constexpr f64_t(double v) noexcept;
```

```cpp
constexpr f64_t(float v) noexcept;
```

```cpp
template <std::integral integral_type_>
constexpr f64_t(integral_type_ v) noexcept;
```

```cpp
template <typename nk_type_>
requires (std::is_class_v<nk_type_> && std::is_convertible_v<nk_type_, float>)
f64_t(nk_type_ v) noexcept;
```

Upcast from any nk numeric wrapper with `operator float()`, such as [f16_t](#f16_t), [bf16_t](#bf16_t), [e4m3_t](#e4m3_t), [i8_t](#i8_t).

## f64c_t

128-bit double-precision complex number, composed of two [f64_t](#f64_t).

```cpp
struct f64c_t
```

Layout: {real: [f64_t](#f64_t), imag: [f64_t](#f64_t)}. Uses composition, not inheritance. Supports complex arithmetic, conjugate, norm, abs, arg, and full transcendentals.

## f64c_t

Upcast from any nk complex wrapper with `.real()` / `.imag()`, such as [f16c_t](#f16c_t), [bf16c_t](#bf16c_t), [f32c_t](#f32c_t).

```cpp
constexpr f64c_t() noexcept;
```

```cpp
constexpr f64c_t(f64_t r) noexcept;
```

```cpp
constexpr f64c_t(double r) noexcept;
```

```cpp
constexpr f64c_t(f64_t r, f64_t i) noexcept;
```

```cpp
constexpr f64c_t(double r, double i) noexcept;
```

```cpp
constexpr f64c_t(raw_t r) noexcept;
```

```cpp
template <typename nk_type_>
requires (!std::is_same_v<nk_type_, f64c_t> && !std::is_same_v<nk_type_, nk_f64c_t> && requires(nk_type_ v) { { static_cast<double>(v.real()) }; { static_cast<double>(v.imag()) }; })
f64c_t(nk_type_ v) noexcept;
```

Upcast from any nk complex wrapper with `.real()` / `.imag()`, such as [f16c_t](#f16c_t), [bf16c_t](#bf16c_t), [f32c_t](#f32c_t).

## cast

Elementwise type-cast from one numeric type to another.

```cpp
template <numeric_dtype from_type_, numeric_dtype to_type_>
status_t cast(
    from_type_ const *from,
    std::size_t n,
    to_type_ *to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

```cpp
template <numeric_dtype from_type_, numeric_dtype to_type_, vector_of<from_type_> from_vector_type_, mutable_vector_of<to_type_> to_vector_type_>
status_t cast(
    from_vector_type_ const & from,
    to_vector_type_ && to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Elementwise type-cast of one run into another of equal dimensions; [`unexpected_dimensions_k`](https://www.unum.cloud/docs/numkong/cpp/quickstart.md#unexpected_dimensions_k) when they differ or either run is strided or ends mid-value.

```cpp
template <typename format_, std::size_t max_rank_>
status_t cast(
    tensor_view<f32_t, max_rank_> from,
    scaled_tensor_span<format_, max_rank_> to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Encode (quantize) a dense f32 tensor into a preallocated block-scaled tensor.

```cpp
template <typename format_>
status_t cast(
    vector_view<f32_t> from,
    scaled_tensor_span<format_> to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Encode, or quantize, a dense f32 vector into a preallocated single-row block-scaled tensor.

```cpp
template <typename format_, std::size_t max_rank_>
status_t cast(
    scaled_tensor_view<format_, max_rank_> from,
    vector_span<f32_t> to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Decode, or dequantize, one block-scaled row into a dense f32 vector.

```cpp
template <typename format_, std::size_t max_rank_>
status_t cast(
    scaled_tensor_view<format_, max_rank_> from,
    tensor_span<f32_t, max_rank_> to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Decode (dequantize) a block-scaled tensor into a dense f32 tensor.

```cpp
template <typename from_format_, typename to_format_, std::size_t max_rank_>
status_t cast(
    scaled_tensor_view<from_format_, max_rank_> from,
    scaled_tensor_span<to_format_, max_rank_> to,
    nk_capability_t capabilities = default_capabilities(),
    nk_stream_t stream = nullptr) noexcept;
```

Transcode one block-scaled tensor into another (e.g.

Derives the per-tensor scale (NVFP4) over the whole tensor in one contiguous pass and writes it back through the destination's scale slot. The source must be contiguous (the scale is a whole-tensor reduction). Allocate the destination with `scaled_tensor<format_>::uninitialized(...)`, which reports the allocation's own status.

- `from` (in): Input array of `n` elements.
- `n` (in): Counts dimensions, a multiple of the values per byte.
- `to` (out): Output array of `n` elements.
- `capabilities` (in): Capabilities to pick from.
- `stream` (in): Null on the CPU, or the stream of the device `capabilities` describes.

## is_numeric_dtype

Detect NumKong wrapper types with required static members.

```cpp
template <typename scalar_type_>
bool is_numeric_dtype() noexcept;
```

## is_integral_dtype

Check if a type is an integer type.

```cpp
template <typename scalar_type_>
bool is_integral_dtype() noexcept;
```

## is_complex_dtype

Check if a type is a complex type - STL or NumKong.

```cpp
template <typename scalar_type_>
bool is_complex_dtype() noexcept;
```

## is_signed_dtype

Check if a type is an complex type - STL or NumKong.

```cpp
template <typename scalar_type_>
bool is_signed_dtype() noexcept;
```

## finite_max

Get the maximum representable value for a type.

```cpp
template <typename scalar_type_>
scalar_type_ finite_max() noexcept;
```

## finite_min

Get the lowest representable value for a type.

```cpp
template <typename scalar_type_>
scalar_type_ finite_min() noexcept;
```

## is_nan

Whether a value is NaN, never true for a type without one.

```cpp
template <numeric_dtype scalar_type_>
bool is_nan(scalar_type_ value) noexcept;
```

## bits_per_value

Bits per value.

```cpp
template <typename scalar_type_>
unsigned bits_per_value() noexcept;
```

For complex types matches value size.

## bits_per_dimension

Bits per value.

```cpp
template <typename scalar_type_>
unsigned bits_per_dimension() noexcept;
```

For complex types matches value size.

## dimensions_per_value

Dimensions per container value.

```cpp
template <typename scalar_type_>
unsigned dimensions_per_value() noexcept;
```

For normal types = 1, for sub-byte packed types > 1.

Block-scaled values, MX and NVFP4, carry a per-block scale byte inside the struct, so `[bits_per_value](#bits_per_value) / [bits_per_dimension](#bits_per_dimension)` would over-count by the scale overhead — NVFP4 is a 9-byte struct holding 16 four-bit dims, so the ratio yields 18, not 16. Every packed/block type exposes the authoritative logical-dimension count via `elements()`; prefer it when available and fall back to the bit ratio, correct at 1, for plain scalar types.

## divide_round_up

Ceiling division, compile-time divisor: (n + divisor - 1) / divisor.

```cpp
template <std::size_t divisor_>
std::size_t divide_round_up(std::size_t n) noexcept;
```

```cpp
std::size_t divide_round_up(
    std::size_t n,
    std::size_t divisor) noexcept;
```

Ceiling division, runtime divisor: (n + divisor - 1) / divisor.

## round_up_to_multiple

Round up to next multiple: ((n + multiple - 1) / multiple) * multiple.

```cpp
template <std::size_t multiple_>
std::size_t round_up_to_multiple(std::size_t n);
```

## fcma

FMA helper template for baseline conjugate complex dot-product implementations.

```cpp
template <typename in_type_, typename accumulator_type_>
requires (dimensions_per_value<in_type_>() == 1)
accumulator_type_ fcma(
    in_type_ a,
    in_type_ b,
    accumulator_type_ acc) noexcept;
```

## saturating_add

Free-standing saturating addition for baseline implementations.

```cpp
template <typename accumulator_type_, typename in_type_>
requires (std::is_same<accumulator_type_, in_type_>::value || dimensions_per_value<in_type_>() == 1)
accumulator_type_ saturating_add(
    accumulator_type_ a,
    in_type_ b) noexcept;
```

```cpp
template <typename accumulator_type_>
accumulator_type_ saturating_add(
    accumulator_type_ a,
    i4x2_t b) noexcept;
```

Saturating addition specialization for [i4x2_t](#i4x2_t), signed 4-bit packed pairs.

```cpp
template <typename accumulator_type_>
accumulator_type_ saturating_add(
    accumulator_type_ a,
    u4x2_t b) noexcept;
```

Saturating addition specialization for [u4x2_t](#u4x2_t), unsigned 4-bit packed pairs.

## saturating_mul

Free-standing saturating multiplication for baseline implementations.

```cpp
template <typename accumulator_type_, typename in_type_>
requires (std::is_same<accumulator_type_, in_type_>::value || dimensions_per_value<in_type_>() == 1)
accumulator_type_ saturating_mul(
    accumulator_type_ a,
    in_type_ b) noexcept;
```

## saturating_fma

Free-standing saturating FMA, a*b + c, for baseline implementations.

```cpp
template <typename in_type_, typename accumulator_type_>
requires fma_operands<in_type_, accumulator_type_>
accumulator_type_ saturating_fma(
    in_type_ a,
    in_type_ b,
    accumulator_type_ acc) noexcept;
```

```cpp
template <typename in_type_, typename accumulator_type_>
requires signed_into_unsigned_fma_operands<in_type_, accumulator_type_>
accumulator_type_ saturating_fma(
    in_type_ a,
    in_type_ b,
    accumulator_type_ acc) noexcept;
```

Saturating FMA of signed operands into an unsigned integer accumulator, multiplied in `[i64_t](#i64_t)` first.

```cpp
template <typename accumulator_type_>
requires (!is_integral_dtype<accumulator_type_>())
accumulator_type_ saturating_fma(
    i4x2_t a,
    i4x2_t b,
    accumulator_type_ acc) noexcept;
```

Saturating FMA specialization for [i4x2_t](#i4x2_t), signed 4-bit packed pairs.

```cpp
template <typename accumulator_type_>
requires (!is_integral_dtype<accumulator_type_>())
accumulator_type_ saturating_fma(
    u4x2_t a,
    u4x2_t b,
    accumulator_type_ acc) noexcept;
```

Saturating FMA specialization for [u4x2_t](#u4x2_t), unsigned 4-bit packed pairs.

## count_differences

Count element-level differences.

```cpp
template <typename scalar_type_>
unsigned count_differences(scalar_type_ a, scalar_type_ b) noexcept;
```

```cpp
unsigned count_differences(u1x8_t a, u1x8_t b) noexcept;
```

Count bit-level differences for [u1x8_t](#u1x8_t), 8 packed bits.

For scalar types, returns 1 if different, 0 if equal.

## count_intersection

Count element-level intersection.

```cpp
template <typename scalar_type_>
unsigned count_intersection(scalar_type_ a, scalar_type_ b) noexcept;
```

```cpp
unsigned count_intersection(u1x8_t a, u1x8_t b) noexcept;
```

Count bit-level intersection for [u1x8_t](#u1x8_t), 8 packed bits.

For scalar types, returns 1 if equal, 0 if different.

## count_union

Count element-level union.

```cpp
template <typename scalar_type_>
unsigned count_union(scalar_type_ a, scalar_type_ b) noexcept;
```

```cpp
unsigned count_union(u1x8_t a, u1x8_t b) noexcept;
```

Count bit-level union for [u1x8_t](#u1x8_t), 8 packed bits.

For scalar types, always returns 1, one element per pair.

