Type Conversions
magetypes provides conversions between SIMD types and between SIMD and scalar types.
Float ↔ Integer Conversions
Float to Integer
#![allow(unused)] fn main() { let floats = f32x8::from_array(token, [1.5, 2.7, -3.2, 4.0, 5.9, 6.1, 7.0, 8.5]); // Truncate toward zero (like `as i32`) let ints = floats.to_i32x8(); // [1, 2, -3, 4, 5, 6, 7, 8] // Round to nearest let rounded = floats.to_i32x8_round(); // [2, 3, -3, 4, 6, 6, 7, 8] }
Integer to Float
#![allow(unused)] fn main() { let ints = i32x8::from_array(token, [1, 2, 3, 4, 5, 6, 7, 8]); let floats = ints.to_f32x8(); // [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
Width Conversions
Narrowing (Wider → Narrower)
#![allow(unused)] fn main() { // f64x4 → f32x4 (lose precision) let doubles = f64x4::from_array(token, [1.0, 2.0, 3.0, 4.0]); let floats = doubles.to_f32x4(); // i32x8 → i16x8 (pack with saturation) let wide = i32x8::from_array(token, [1, 2, 3, 4, 5, 6, 7, 8]); let narrow = wide.pack_i16(); }
Widening (Narrower → Wider)
#![allow(unused)] fn main() { // f32x4 → f64x4 (extend precision, lower half) let floats = f32x4::from_array(token, [1.0, 2.0, 3.0, 4.0]); let doubles = floats.to_f64x4_low(); // Converts first 2 elements // i16x8 → i32x8 (sign-extend lower half) let narrow = i16x8::from_array(token, [1, 2, 3, 4, 5, 6, 7, 8]); let wide = narrow.extend_i32_low(); // [1, 2, 3, 4] }
Bitcast (Reinterpret)
Reinterpret bits as a different type (same size):
#![allow(unused)] fn main() { // f32x8 → i32x8 (view float bits as integers) let floats = f32x8::splat(token, 1.0); let bits = floats.bitcast_i32x8(); // i32x8 → f32x8 let ints = i32x8::splat(token, 0x3f800000); // IEEE 754 for 1.0 let floats = ints.bitcast_f32x8(); }
Warning: Bitcast doesn't convert values—it reinterprets the raw bits.
Signed ↔ Unsigned
#![allow(unused)] fn main() { // i32x8 → u32x8 (reinterpret, no conversion) let signed = i32x8::from_array(token, [-1, 0, 1, 2, 3, 4, 5, 6]); let unsigned = signed.to_u32x8(); // [0xFFFFFFFF, 0, 1, 2, 3, 4, 5, 6] // u32x8 → i32x8 let unsigned = u32x8::splat(token, 0xFFFFFFFF); let signed = unsigned.to_i32x8(); // [-1; 8] }
Lane Extraction and Insertion
#![allow(unused)] fn main() { // Extract single lane let v = f32x8::from_array(token, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]); let third = v.extract::<2>(); // 3.0 // Insert single lane let v = v.insert::<2>(99.0); // [1.0, 2.0, 99.0, 4.0, 5.0, 6.0, 7.0, 8.0] }
Half-Width Operations
Split or combine vectors:
#![allow(unused)] fn main() { // Split f32x8 into two f32x4 let full = f32x8::from_array(token, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]); let (low, high) = full.split(); // low = [1.0, 2.0, 3.0, 4.0] // high = [5.0, 6.0, 7.0, 8.0] // Combine two f32x4 into f32x8 let combined = f32x8::from_halves(token, low, high); }
Slice Casting (Token-Gated)
magetypes provides safe, token-gated slice casting as an alternative to bytemuck:
#![allow(unused)] fn main() { // Cast aligned &[f32] to &[f32x8] let data: &[f32] = &[1.0; 64]; if let Some(chunks) = f32x8::cast_slice(token, data) { // chunks: &[f32x8] with 8 elements for chunk in chunks { // ... } } // Mutable version let data: &mut [f32] = &mut [0.0; 64]; if let Some(chunks) = f32x8::cast_slice_mut(token, data) { // chunks: &mut [f32x8] } }
Why not bytemuck? Implementing Pod/Zeroable would let users bypass token-gated construction:
#![allow(unused)] fn main() { // bytemuck would allow this (BAD): let v: f32x8 = bytemuck::Zeroable::zeroed(); // No token check! // magetypes requires token (GOOD): let v = f32x8::zero(token); // Token proves CPU support }
The token-gated cast_slice returns None if alignment or length is wrong—no UB possible.
Byte-Level Access
View vectors as bytes (no token needed—you already have the vector):
#![allow(unused)] fn main() { let v = f32x8::splat(token, 1.0); // View as bytes (zero-cost) let bytes: &[u8; 32] = v.as_bytes(); // Mutable view let mut v = f32x8::splat(token, 0.0); let bytes: &mut [u8; 32] = v.as_bytes_mut(); // Create from bytes (token-gated) let bytes = [0u8; 32]; let v = f32x8::from_bytes(token, &bytes); }
Conversion Example: Image Processing
#![allow(unused)] fn main() { #[arcane] fn brighten(token: Desktop64, pixels: &mut [u8]) { // Process 32 bytes at a time for chunk in pixels.chunks_exact_mut(32) { let v = u8x32::from_slice(token, chunk); // Convert to wider type for arithmetic let (lo, hi) = v.split(); let lo_wide = lo.extend_u16_low(); let hi_wide = hi.extend_u16_low(); // Add brightness (with saturation) let brightness = u16x16::splat(token, 20); let lo_bright = lo_wide.saturating_add(brightness); let hi_bright = hi_wide.saturating_add(brightness); // Pack back to u8 with saturation let result = u8x32::from_halves( token, lo_bright.pack_u8_saturate(), hi_bright.pack_u8_saturate() ); result.store_slice(chunk); } } }