Float16Array - Opaque Type Implementation #
High-performance Float16 (half precision) array with hardware-accelerated SIMD and GPU interop.
Hardware Requirements #
x86_64: F16C extension (Intel Ivy Bridge+ / AMD Bulldozer+) ARM64: ARMv8.2-A with FP16 vector arithmetic (Apple M1+, AWS Graviton2+)
Fallback: If hardware FP16 is unavailable, operations return errors.
Use hasHardwareSupport to check before using.
Key Advantages #
- 4x Memory Savings: vs Float64, 2x vs Float32 (2 bytes/element)
- Massive SIMD Width: NEON 8 halfs/op, AVX2+F16C 8 halfs/op
- GPU Tensor Bandwidth: Modern GPUs prefer FP16 for bandwidth-limited ops
- Zero Copy: Direct pointer passing to WebGPU
- In-Place Updates: Mutable operations (no array copying)
Implementation Pattern #
- Lean Side: Opaque type (contents invisible)
- C++ Side:
std::vector<uint16_t>(FP16 stored as raw bits) - Conversion: C++ handles f64 ↔ f16 conversion via
_cvtss_sh/_cvtsh_ss(x86) orvcvt(ARM) - GPU Interop: Pass raw pointer directly
Usage #
-- Check hardware support first!
let hasF16 ← Float16Array.hasHardwareSupport
if hasF16 then
let arr ← Float16Array.create 1000
arr.set 0 3.14
let val ← arr.get 0
-- Pass to GPU
let ptr ← arr.ptr
let size ← arr.byteSize
else
-- Fall back to Float32
IO.println "FP16 not supported, using Float32"
Type Definition #
Opaque type representing a C++ std::vector<uint16_t> storing FP16 values. Contents are invisible to Lean - all operations go through FFI.
Float16 array type (opaque to Lean, managed by C++)
Instances For
Hardware Detection #
Check if FP16 hardware support is available.
Returns:
true: F16C (x86) or FP16 (ARM) availablefalse: No hardware support, Float16 operations will fail
Detection Method:
- x86_64: Check CPUID for F16C bit
- ARM64: Check HWCAP for FPHP/ASIMDHP flags
Creation and Destruction #
Create a new Float16Array with the specified number of elements. All elements are initialized to 0.0 (FP16 zero).
C++ Implementation: new std::vector<uint16_t>(size, 0)
Requires: FP16 hardware support (returns error if unavailable)
Element Access #
Set a value at the specified index (in-place mutation). Value is converted from Float64 to Float16 (may lose precision).
Precision Loss: F64 has 53-bit mantissa, F16 has 10-bit mantissa. Large values or high precision values will be rounded.
Safety: Out-of-bounds access returns error. Performance: O(1), in-place update (no array copy).
Get a value at the specified index. Value is converted from Float16 to Float64 (exact conversion).
Safety: Out-of-bounds access returns error. Performance: O(1).
Array Properties #
Get the number of Float16 elements in the array.
Get the size in bytes (for GPU buffer allocation).
GPU Interop (Zero-Copy Pointer Access) #
Get the raw pointer to the underlying data for GPU operations.
UNSAFE: Returns raw pointer (size_t) to vec.data().
Valid only while Float16Array object is alive.
Use Case: Zero-copy GPU tensor uploads:
let ptr ← arr.ptr
let size ← arr.byteSize
-- C++: wgpuQueueWriteBuffer(queue, buffer, 0, (void*)ptr, size)
Safety:
- Keep Float16Array alive during GPU operation
- No concurrent modifications while GPU reads
Conversions #
Create Float16Array from Lean's native Array Float.
Precision Loss: Each Float64 → Float16 conversion may lose precision. Performance: O(n), uses software FP16 conversion (hardware acceleration TODO).
Convert Float16Array to Lean's native Array Float.
Precision: Float16 → Float64 is exact (no loss). Performance: O(n), uses software FP16 conversion (hardware acceleration TODO).
Create Float16Array from Float32Array.
Use Case: GPU tensor pipelines often prefer FP16 for bandwidth. Performance: O(n), hardware-accelerated conversion.
Convert Float16Array to Float32Array.
Use Case: Mixed-precision training (FP16 forward, FP32 backward). Performance: O(n), hardware-accelerated conversion.
SIMD Operations #
SIMD element-wise addition: result[i] = a[i] + b[i]
Hardware Acceleration:
- x86_64: AVX2 + F16C (8 halfs/operation)
- ARM64: NEON FP16 (8 halfs/operation)
Safety: Arrays must have same size. Performance: ~8x faster than scalar.
SIMD element-wise multiplication: result[i] = a[i] * b[i]
Utilities #
String representation for debugging
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- Hesper.Core.instToStringFloat16Array = { toString := fun (_arr : Hesper.Core.Float16Array) => toString "Float16Array[size=?] (use arr.toString for details)" }