//! Contains utility functions and traits to convert between slices of [`u16`] bits and [`f16`] or //! [`bf16`] numbers. //! //! The utility [`HalfBitsSliceExt`] sealed extension trait is implemented for `[u16]` slices, //! while the utility [`HalfFloatSliceExt`] sealed extension trait is implemented for both `[f16]` //! and `[bf16]` slices. 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.
usecrate::{bf16, binary16::convert, f16}; #[cfg(feature = "alloc")] use alloc::vec::Vec; use core::slice;
/// Extensions to `[f16]` and `[bf16]` slices to support conversion and reinterpret operations. /// /// This trait is sealed and cannot be implemented outside of this crate. pubtrait HalfFloatSliceExt: private::SealedHalfFloatSlice { /// Reinterprets a slice of [`f16`] or [`bf16`] numbers as a slice of [`u16`] bits. /// /// This is a zero-copy operation. The reinterpreted slice has the same lifetime and memory /// location as `self`. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let float_buffer = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]; /// let int_buffer = float_buffer.reinterpret_cast(); /// /// assert_eq!(int_buffer, [float_buffer[0].to_bits(), float_buffer[1].to_bits(), float_buffer[2].to_bits()]); /// ``` fn reinterpret_cast(&self) -> &[u16];
/// Reinterprets a mutable slice of [`f16`] or [`bf16`] numbers as a mutable slice of [`u16`]. /// bits /// /// This is a zero-copy operation. The transmuted slice has the same lifetime as the original, /// which prevents mutating `self` as long as the returned `&mut [u16]` is borrowed. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let mut float_buffer = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]; /// /// { /// let int_buffer = float_buffer.reinterpret_cast_mut(); /// /// assert_eq!(int_buffer, [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]); /// /// // Mutating the u16 slice will mutating the original /// int_buffer[0] = 0; /// } /// /// // Note that we need to drop int_buffer before using float_buffer again or we will get a borrow error. /// assert_eq!(float_buffer, [f16::from_f32(0.), f16::from_f32(2.), f16::from_f32(3.)]); /// ``` fn reinterpret_cast_mut(&mutself) -> &mut [u16];
/// Converts all of the elements of a `[f32]` slice into [`f16`] or [`bf16`] values in `self`. /// /// The length of `src` must be the same as `self`. /// /// 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. /// /// # Panics /// /// This function will panic if the two slices have different lengths. /// /// # Examples /// ```rust /// # use half::prelude::*; /// // Initialize an empty buffer /// let mut buffer = [0u16; 4]; /// let buffer = buffer.reinterpret_cast_mut::<f16>(); /// /// let float_values = [1., 2., 3., 4.]; /// /// // Now convert /// buffer.convert_from_f32_slice(&float_values); /// /// assert_eq!(buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)]); /// ``` fn convert_from_f32_slice(&mutself, src: &[f32]);
/// Converts all of the elements of a `[f64]` slice into [`f16`] or [`bf16`] values in `self`. /// /// The length of `src` must be the same as `self`. /// /// 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. /// /// # Panics /// /// This function will panic if the two slices have different lengths. /// /// # Examples /// ```rust /// # use half::prelude::*; /// // Initialize an empty buffer /// let mut buffer = [0u16; 4]; /// let buffer = buffer.reinterpret_cast_mut::<f16>(); /// /// let float_values = [1., 2., 3., 4.]; /// /// // Now convert /// buffer.convert_from_f64_slice(&float_values); /// /// assert_eq!(buffer, [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)]); /// ``` fn convert_from_f64_slice(&mutself, src: &[f64]);
/// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f32`] values in `dst`. /// /// The length of `src` must be the same as `self`. /// /// 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. /// /// # Panics /// /// This function will panic if the two slices have different lengths. /// /// # Examples /// ```rust /// # use half::prelude::*; /// // Initialize an empty buffer /// let mut buffer = [0f32; 4]; /// /// let half_values = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)]; /// /// // Now convert /// half_values.convert_to_f32_slice(&mut buffer); /// /// assert_eq!(buffer, [1., 2., 3., 4.]); /// ``` fn convert_to_f32_slice(&self, dst: &mut [f32]);
/// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f64`] values in `dst`. /// /// The length of `src` must be the same as `self`. /// /// 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. /// /// # Panics /// /// This function will panic if the two slices have different lengths. /// /// # Examples /// ```rust /// # use half::prelude::*; /// // Initialize an empty buffer /// let mut buffer = [0f64; 4]; /// /// let half_values = [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)]; /// /// // Now convert /// half_values.convert_to_f64_slice(&mut buffer); /// /// assert_eq!(buffer, [1., 2., 3., 4.]); /// ``` fn convert_to_f64_slice(&self, dst: &mut [f64]);
// Because trait is sealed, we can get away with different interfaces between features.
/// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f32`] values in a new /// 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. /// /// This method is only available with the `std` or `alloc` feature. /// /// # Examples /// ```rust /// # use half::prelude::*; /// let half_values = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)]; /// let vec = half_values.to_f32_vec(); /// /// assert_eq!(vec, vec![1., 2., 3., 4.]); /// ``` #[cfg(any(feature = "alloc", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn to_f32_vec(&self) -> Vec<f32>;
/// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f64`] values in a new /// 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. /// /// This method is only available with the `std` or `alloc` feature. /// /// # Examples /// ```rust /// # use half::prelude::*; /// let half_values = [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)]; /// let vec = half_values.to_f64_vec(); /// /// assert_eq!(vec, vec![1., 2., 3., 4.]); /// ``` #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn to_f64_vec(&self) -> Vec<f64>;
}
/// Extensions to `[u16]` slices to support reinterpret operations. /// /// This trait is sealed and cannot be implemented outside of this crate. pubtrait HalfBitsSliceExt: private::SealedHalfBitsSlice { /// Reinterprets a slice of [`u16`] bits as a slice 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 slice has the same lifetime and memory /// location as `self`. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let int_buffer = [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]; /// let float_buffer: &[f16] = int_buffer.reinterpret_cast(); /// /// assert_eq!(float_buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]); /// /// // You may have to specify the cast type directly if the compiler can't infer the type. /// // The following is also valid in Rust. /// let typed_buffer = int_buffer.reinterpret_cast::<f16>(); /// ``` fn reinterpret_cast<H>(&self) -> &[H] where
H: crate::private::SealedHalf;
/// Reinterprets a mutable slice of [`u16`] bits as a mutable slice 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 transmuted slice has the same lifetime as the original, /// which prevents mutating `self` as long as the returned `&mut [f16]` is borrowed. /// /// # Examples /// /// ```rust /// # use half::prelude::*; /// let mut int_buffer = [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]; /// /// { /// let float_buffer: &mut [f16] = int_buffer.reinterpret_cast_mut(); /// /// assert_eq!(float_buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]); /// /// // Mutating the f16 slice will mutating the original /// float_buffer[0] = f16::from_f32(0.); /// } /// /// // Note that we need to drop float_buffer before using int_buffer again or we will get a borrow error. /// assert_eq!(int_buffer, [f16::from_f32(0.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]); /// /// // You may have to specify the cast type directly if the compiler can't infer the type. /// // The following is also valid in Rust. /// let typed_buffer = int_buffer.reinterpret_cast_mut::<f16>(); /// ``` fn reinterpret_cast_mut<H>(&mutself) -> &mut [H] where
H: crate::private::SealedHalf;
}
mod private { usecrate::{bf16, f16};
pubtrait SealedHalfFloatSlice {} impl SealedHalfFloatSlice for [f16] {} impl SealedHalfFloatSlice for [bf16] {}
pubtrait SealedHalfBitsSlice {} impl SealedHalfBitsSlice for [u16] {}
}
impl HalfFloatSliceExt for [f16] { #[inline] fn reinterpret_cast(&self) -> &[u16] { let pointer = self.as_ptr() as *const u16; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts(pointer, length) }
}
#[inline] fn reinterpret_cast_mut(&mutself) -> &mut [u16] { let pointer = self.as_ptr() as *mut u16; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts_mut(pointer, length) }
}
fn convert_from_f32_slice(&mutself, src: &[f32]) {
assert_eq!( self.len(),
src.len(), "destination and source slices have different lengths"
);
letmut chunks = src.chunks_exact(4); letmut chunk_count = 0usize; // Not using .enumerate() because we need this value for remainder for chunk in &mut chunks { let vec = convert::f32x4_to_f16x4(chunk); let dst_idx = chunk_count * 4; self[dst_idx..dst_idx + 4].copy_from_slice(vec.reinterpret_cast());
chunk_count += 1;
}
// Process remainder if !chunks.remainder().is_empty() { letmut buf = [0f32; 4];
buf[..chunks.remainder().len()].copy_from_slice(chunks.remainder()); let vec = convert::f32x4_to_f16x4(&buf); let dst_idx = chunk_count * 4; self[dst_idx..dst_idx + chunks.remainder().len()]
.copy_from_slice(vec[..chunks.remainder().len()].reinterpret_cast());
}
}
fn convert_from_f64_slice(&mutself, src: &[f64]) {
assert_eq!( self.len(),
src.len(), "destination and source slices have different lengths"
);
letmut chunks = src.chunks_exact(4); letmut chunk_count = 0usize; // Not using .enumerate() because we need this value for remainder for chunk in &mut chunks { let vec = convert::f64x4_to_f16x4(chunk); let dst_idx = chunk_count * 4; self[dst_idx..dst_idx + 4].copy_from_slice(vec.reinterpret_cast());
chunk_count += 1;
}
// Process remainder if !chunks.remainder().is_empty() { letmut buf = [0f64; 4];
buf[..chunks.remainder().len()].copy_from_slice(chunks.remainder()); let vec = convert::f64x4_to_f16x4(&buf); let dst_idx = chunk_count * 4; self[dst_idx..dst_idx + chunks.remainder().len()]
.copy_from_slice(vec[..chunks.remainder().len()].reinterpret_cast());
}
}
fn convert_to_f32_slice(&self, dst: &mut [f32]) {
assert_eq!( self.len(),
dst.len(), "destination and source slices have different lengths"
);
letmut chunks = self.chunks_exact(4); letmut chunk_count = 0usize; // Not using .enumerate() because we need this value for remainder for chunk in &mut chunks { let vec = convert::f16x4_to_f32x4(chunk.reinterpret_cast()); let dst_idx = chunk_count * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&vec);
chunk_count += 1;
}
// Process remainder if !chunks.remainder().is_empty() { letmut buf = [0u16; 4];
buf[..chunks.remainder().len()].copy_from_slice(chunks.remainder().reinterpret_cast()); let vec = convert::f16x4_to_f32x4(&buf); let dst_idx = chunk_count * 4;
dst[dst_idx..dst_idx + chunks.remainder().len()]
.copy_from_slice(&vec[..chunks.remainder().len()]);
}
}
fn convert_to_f64_slice(&self, dst: &mut [f64]) {
assert_eq!( self.len(),
dst.len(), "destination and source slices have different lengths"
);
letmut chunks = self.chunks_exact(4); letmut chunk_count = 0usize; // Not using .enumerate() because we need this value for remainder for chunk in &mut chunks { let vec = convert::f16x4_to_f64x4(chunk.reinterpret_cast()); let dst_idx = chunk_count * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&vec);
chunk_count += 1;
}
// Process remainder if !chunks.remainder().is_empty() { letmut buf = [0u16; 4];
buf[..chunks.remainder().len()].copy_from_slice(chunks.remainder().reinterpret_cast()); let vec = convert::f16x4_to_f64x4(&buf); let dst_idx = chunk_count * 4;
dst[dst_idx..dst_idx + chunks.remainder().len()]
.copy_from_slice(&vec[..chunks.remainder().len()]);
}
}
#[cfg(any(feature = "alloc", feature = "std"))] #[inline] fn to_f32_vec(&self) -> Vec<f32> { letmut vec = Vec::with_capacity(self.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(self.len()) }; self.convert_to_f32_slice(&mut vec);
vec
}
#[cfg(any(feature = "alloc", feature = "std"))] #[inline] fn to_f64_vec(&self) -> Vec<f64> { letmut vec = Vec::with_capacity(self.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(self.len()) }; self.convert_to_f64_slice(&mut vec);
vec
}
}
impl HalfFloatSliceExt for [bf16] { #[inline] fn reinterpret_cast(&self) -> &[u16] { let pointer = self.as_ptr() as *const u16; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts(pointer, length) }
}
#[inline] fn reinterpret_cast_mut(&mutself) -> &mut [u16] { let pointer = self.as_ptr() as *mut u16; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts_mut(pointer, length) }
}
fn convert_from_f32_slice(&mutself, src: &[f32]) {
assert_eq!( self.len(),
src.len(), "destination and source slices have different lengths"
);
// Just use regular loop here until there's any bf16 SIMD support. for (i, f) in src.iter().enumerate() { self[i] = bf16::from_f32(*f);
}
}
fn convert_from_f64_slice(&mutself, src: &[f64]) {
assert_eq!( self.len(),
src.len(), "destination and source slices have different lengths"
);
// Just use regular loop here until there's any bf16 SIMD support. for (i, f) in src.iter().enumerate() { self[i] = bf16::from_f64(*f);
}
}
fn convert_to_f32_slice(&self, dst: &mut [f32]) {
assert_eq!( self.len(),
dst.len(), "destination and source slices have different lengths"
);
// Just use regular loop here until there's any bf16 SIMD support. for (i, f) inself.iter().enumerate() {
dst[i] = f.to_f32();
}
}
fn convert_to_f64_slice(&self, dst: &mut [f64]) {
assert_eq!( self.len(),
dst.len(), "destination and source slices have different lengths"
);
// Just use regular loop here until there's any bf16 SIMD support. for (i, f) inself.iter().enumerate() {
dst[i] = f.to_f64();
}
}
#[cfg(any(feature = "alloc", feature = "std"))] #[inline] fn to_f32_vec(&self) -> Vec<f32> { letmut vec = Vec::with_capacity(self.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(self.len()) }; self.convert_to_f32_slice(&mut vec);
vec
}
#[cfg(any(feature = "alloc", feature = "std"))] #[inline] fn to_f64_vec(&self) -> Vec<f64> { letmut vec = Vec::with_capacity(self.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(self.len()) }; self.convert_to_f64_slice(&mut vec);
vec
}
}
impl HalfBitsSliceExt for [u16] { // Since we sealed all the traits involved, these are safe. #[inline] fn reinterpret_cast<H>(&self) -> &[H] where
H: crate::private::SealedHalf,
{ let pointer = self.as_ptr() as *const H; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts(pointer, length) }
}
#[inline] fn reinterpret_cast_mut<H>(&mutself) -> &mut [H] where
H: crate::private::SealedHalf,
{ let pointer = self.as_mut_ptr() as *mut H; let length = self.len(); // SAFETY: We are reconstructing full length of original slice, using its same lifetime, // and the size of elements are identical unsafe { slice::from_raw_parts_mut(pointer, length) }
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.