v6.5: Half-Precision `f16` & `bf16` Numbers in Rust π¦
SimSIMD has historically been one of the largest collections of mixed-precision kernels, but f32 to/from f16 and bf16 conversion operators have never been exposed to bindings. This release is the first step in that direction. I look forward to everyone's suggestions on how to further improve the Rust API. Thanks π€
Here's an example:
use simsimd::{SpatialSimilarity, f16, bf16};
// Process embeddings at different precisions for speed vs accuracy trade-offs
let embeddings_f32 = vec![1.0, 2.0, 3.0, 4.0];
let query_f32 = vec![0.5, 1.5, 2.5, 3.5];
// Convert to f16 for memory efficiency (2x compression)
let embeddings_f16: Vec<f16> = embeddings_f32.iter()
.map(|&x| f16::from_f32(x))
.collect();
let query_f16: Vec<f16> = query_f32.iter()
.map(|&x| f16::from_f32(x))
.collect();
// Convert to bf16 for ML workloads (better range than f16)
let embeddings_bf16: Vec<bf16> = embeddings_f32.iter()
.map(|&x| bf16::from_f32(x))
.collect();
let query_bf16: Vec<bf16> = query_f32.iter()
.map(|&x| bf16::from_f32(x))
.collect();
// Hardware-accelerated similarity at different precisions
let similarity_f32 = f32::cosine(&embeddings_f32, &query_f32).unwrap();
let similarity_f16 = f16::cosine(&embeddings_f16, &query_f16).unwrap();
let similarity_bf16 = bf16::cosine(&embeddings_bf16, &query_bf16).unwrap();
println!("Cosine similarities:");
println!("f32: {:.6}", similarity_f32); // Full precision
println!("f16: {:.6}", similarity_f16); // 2x memory savings
println!("bf16: {:.6}", similarity_bf16); // ML-optimized range
// Natural arithmetic operations work seamlessly
let scaled_f16 = embeddings_f16.iter()
.map(|&x| x * f16::from_f32(2.0) + f16::ONE)
.collect::<Vec<_>>();
// Direct bit manipulation when needed
let raw_bits = embeddings_f16[0].0; // Access underlying u16
let reconstructed = f16(raw_bits);Minor
- Add: Half-precision converters for C/Rust (bf5a7d2)