//! Contains utility functions and traits to convert between vectors of [`u16`] bits and [`f16`] or //! [`bf16`] vectors. //! //! The utility [`HalfBitsVecExt`] sealed extension trait is implemented for [`Vec`] vectors, //! while the utility [`HalfFloatVecExt`] sealed extension trait is implemented for both //! [`Vec`] and [`Vec`] vectors. These traits provide efficient conversions and //! reinterpret casting of larger buffers of floating point values, and are automatically included //! in the [`prelude`][crate::prelude] module. //! //! This module is only available with the `std` or `alloc` feature. use super::{bf16, f16, slice::HalfFloatSliceExt}; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::mem; /// Extensions to [`Vec`] and [`Vec`] to support reinterpret operations. /// /// This trait is sealed and cannot be implemented outside of this crate. pub trait HalfFloatVecExt: private::SealedHalfFloatVec { /// Reinterprets a vector of [`f16`]or [`bf16`] numbers as a vector of [`u16`] bits. /// /// This is a zero-copy operation. The reinterpreted vector has the same memory location as /// `self`. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let float_buffer = vec![f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]; /// let int_buffer = float_buffer.reinterpret_into(); /// /// assert_eq!(int_buffer, [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]); /// ``` #[must_use] fn reinterpret_into(self) -> Vec; /// Converts all of the elements of a `[f32]` slice into a new [`f16`] or [`bf16`] vector. /// /// The conversion operation is vectorized over the slice, meaning the conversion may be more /// efficient than converting individual elements on some hardware that supports SIMD /// conversions. See [crate documentation][crate] for more information on hardware conversion /// support. /// /// # Examples /// ```rust /// # use half::prelude::*; /// let float_values = [1., 2., 3., 4.]; /// let vec: Vec = Vec::from_f32_slice(&float_values); /// /// assert_eq!(vec, vec![f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)]); /// ``` #[must_use] fn from_f32_slice(slice: &[f32]) -> Self; /// Converts all of the elements of a `[f64]` slice into a new [`f16`] or [`bf16`] vector. /// /// The conversion operation is vectorized over the slice, meaning the conversion may be more /// efficient than converting individual elements on some hardware that supports SIMD /// conversions. See [crate documentation][crate] for more information on hardware conversion /// support. /// /// # Examples /// ```rust /// # use half::prelude::*; /// let float_values = [1., 2., 3., 4.]; /// let vec: Vec = Vec::from_f64_slice(&float_values); /// /// assert_eq!(vec, vec![f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)]); /// ``` #[must_use] fn from_f64_slice(slice: &[f64]) -> Self; } /// Extensions to [`Vec`] to support reinterpret operations. /// /// This trait is sealed and cannot be implemented outside of this crate. pub trait HalfBitsVecExt: private::SealedHalfBitsVec { /// Reinterprets a vector of [`u16`] bits as a vector of [`f16`] or [`bf16`] numbers. /// /// `H` is the type to cast to, and must be either the [`f16`] or [`bf16`] type. /// /// This is a zero-copy operation. The reinterpreted vector has the same memory location as /// `self`. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let int_buffer = vec![f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]; /// let float_buffer = int_buffer.reinterpret_into::(); /// /// assert_eq!(float_buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]); /// ``` #[must_use] fn reinterpret_into(self) -> Vec where H: crate::private::SealedHalf; } mod private { use crate::{bf16, f16}; #[cfg(feature = "alloc")] use alloc::vec::Vec; pub trait SealedHalfFloatVec {} impl SealedHalfFloatVec for Vec {} impl SealedHalfFloatVec for Vec {} pub trait SealedHalfBitsVec {} impl SealedHalfBitsVec for Vec {} } impl HalfFloatVecExt for Vec { #[inline] fn reinterpret_into(mut self) -> Vec { // An f16 array has same length and capacity as u16 array let length = self.len(); let capacity = self.capacity(); // Actually reinterpret the contents of the Vec as u16, // knowing that structs are represented as only their members in memory, // which is the u16 part of `f16(u16)` let pointer = self.as_mut_ptr() as *mut u16; // Prevent running a destructor on the old Vec, so the pointer won't be deleted mem::forget(self); // Finally construct a new Vec from the raw pointer // SAFETY: We are reconstructing full length and capacity of original vector, // using its original pointer, and the size of elements are identical. unsafe { Vec::from_raw_parts(pointer, length, capacity) } } #[allow(clippy::uninit_vec)] fn from_f32_slice(slice: &[f32]) -> Self { let mut vec = Vec::with_capacity(slice.len()); // SAFETY: convert will initialize every value in the vector without reading them, // so this is safe to do instead of double initialize from resize, and we're setting it to // same value as capacity. unsafe { vec.set_len(slice.len()) }; vec.convert_from_f32_slice(slice); vec } #[allow(clippy::uninit_vec)] fn from_f64_slice(slice: &[f64]) -> Self { let mut vec = Vec::with_capacity(slice.len()); // SAFETY: convert will initialize every value in the vector without reading them, // so this is safe to do instead of double initialize from resize, and we're setting it to // same value as capacity. unsafe { vec.set_len(slice.len()) }; vec.convert_from_f64_slice(slice); vec } } impl HalfFloatVecExt for Vec { #[inline] fn reinterpret_into(mut self) -> Vec { // An f16 array has same length and capacity as u16 array let length = self.len(); let capacity = self.capacity(); // Actually reinterpret the contents of the Vec as u16, // knowing that structs are represented as only their members in memory, // which is the u16 part of `f16(u16)` let pointer = self.as_mut_ptr() as *mut u16; // Prevent running a destructor on the old Vec, so the pointer won't be deleted mem::forget(self); // Finally construct a new Vec from the raw pointer // SAFETY: We are reconstructing full length and capacity of original vector, // using its original pointer, and the size of elements are identical. unsafe { Vec::from_raw_parts(pointer, length, capacity) } } #[allow(clippy::uninit_vec)] fn from_f32_slice(slice: &[f32]) -> Self { let mut vec = Vec::with_capacity(slice.len()); // SAFETY: convert will initialize every value in the vector without reading them, // so this is safe to do instead of double initialize from resize, and we're setting it to // same value as capacity. unsafe { vec.set_len(slice.len()) }; vec.convert_from_f32_slice(slice); vec } #[allow(clippy::uninit_vec)] fn from_f64_slice(slice: &[f64]) -> Self { let mut vec = Vec::with_capacity(slice.len()); // SAFETY: convert will initialize every value in the vector without reading them, // so this is safe to do instead of double initialize from resize, and we're setting it to // same value as capacity. unsafe { vec.set_len(slice.len()) }; vec.convert_from_f64_slice(slice); vec } } impl HalfBitsVecExt for Vec { // This is safe because all traits are sealed #[inline] fn reinterpret_into(mut self) -> Vec where H: crate::private::SealedHalf, { // An f16 array has same length and capacity as u16 array let length = self.len(); let capacity = self.capacity(); // Actually reinterpret the contents of the Vec as f16, // knowing that structs are represented as only their members in memory, // which is the u16 part of `f16(u16)` let pointer = self.as_mut_ptr() as *mut H; // Prevent running a destructor on the old Vec, so the pointer won't be deleted mem::forget(self); // Finally construct a new Vec from the raw pointer // SAFETY: We are reconstructing full length and capacity of original vector, // using its original pointer, and the size of elements are identical. unsafe { Vec::from_raw_parts(pointer, length, capacity) } } } #[cfg(test)] mod test { use super::{HalfBitsVecExt, HalfFloatVecExt}; use crate::{bf16, f16}; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec; #[test] fn test_vec_conversions_f16() { let numbers = vec![f16::E, f16::PI, f16::EPSILON, f16::FRAC_1_SQRT_2]; let bits = vec![ f16::E.to_bits(), f16::PI.to_bits(), f16::EPSILON.to_bits(), f16::FRAC_1_SQRT_2.to_bits(), ]; let bits_cloned = bits.clone(); // Convert from bits to numbers let from_bits = bits.reinterpret_into::(); assert_eq!(&from_bits[..], &numbers[..]); // Convert from numbers back to bits let to_bits = from_bits.reinterpret_into(); assert_eq!(&to_bits[..], &bits_cloned[..]); } #[test] fn test_vec_conversions_bf16() { let numbers = vec![bf16::E, bf16::PI, bf16::EPSILON, bf16::FRAC_1_SQRT_2]; let bits = vec![ bf16::E.to_bits(), bf16::PI.to_bits(), bf16::EPSILON.to_bits(), bf16::FRAC_1_SQRT_2.to_bits(), ]; let bits_cloned = bits.clone(); // Convert from bits to numbers let from_bits = bits.reinterpret_into::(); assert_eq!(&from_bits[..], &numbers[..]); // Convert from numbers back to bits let to_bits = from_bits.reinterpret_into(); assert_eq!(&to_bits[..], &bits_cloned[..]); } }