| //! 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. |
| |
| use crate::{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. |
| pub trait 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(&mut self) -> &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(&mut self, 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(&mut self, 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. |
| pub trait 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>(&mut self) -> &mut [H] |
| where |
| H: crate::private::SealedHalf; |
| } |
| |
| mod private { |
| use crate::{bf16, f16}; |
| |
| pub trait SealedHalfFloatSlice {} |
| impl SealedHalfFloatSlice for [f16] {} |
| impl SealedHalfFloatSlice for [bf16] {} |
| |
| pub trait 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(&mut self) -> &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(&mut self, src: &[f32]) { |
| assert_eq!( |
| self.len(), |
| src.len(), |
| "destination and source slices have different lengths" |
| ); |
| |
| let mut chunks = src.chunks_exact(4); |
| let mut 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() { |
| let mut 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(&mut self, src: &[f64]) { |
| assert_eq!( |
| self.len(), |
| src.len(), |
| "destination and source slices have different lengths" |
| ); |
| |
| let mut chunks = src.chunks_exact(4); |
| let mut 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() { |
| let mut 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" |
| ); |
| |
| let mut chunks = self.chunks_exact(4); |
| let mut 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() { |
| let mut 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" |
| ); |
| |
| let mut chunks = self.chunks_exact(4); |
| let mut 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() { |
| let mut 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> { |
| let mut 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> { |
| let mut 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(&mut self) -> &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(&mut self, 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(&mut self, 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) in self.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) in self.iter().enumerate() { |
| dst[i] = f.to_f64(); |
| } |
| } |
| |
| #[cfg(any(feature = "alloc", feature = "std"))] |
| #[inline] |
| fn to_f32_vec(&self) -> Vec<f32> { |
| let mut 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> { |
| let mut 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>(&mut self) -> &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) } |
| } |
| } |
| |
| #[doc(hidden)] |
| #[deprecated( |
| since = "1.4.0", |
| note = "use `HalfBitsSliceExt::reinterpret_cast_mut` instead" |
| )] |
| #[inline] |
| pub fn from_bits_mut(bits: &mut [u16]) -> &mut [f16] { |
| bits.reinterpret_cast_mut() |
| } |
| |
| #[doc(hidden)] |
| #[deprecated( |
| since = "1.4.0", |
| note = "use `HalfFloatSliceExt::reinterpret_cast_mut` instead" |
| )] |
| #[inline] |
| pub fn to_bits_mut(bits: &mut [f16]) -> &mut [u16] { |
| bits.reinterpret_cast_mut() |
| } |
| |
| #[doc(hidden)] |
| #[deprecated( |
| since = "1.4.0", |
| note = "use `HalfBitsSliceExt::reinterpret_cast` instead" |
| )] |
| #[inline] |
| pub fn from_bits(bits: &[u16]) -> &[f16] { |
| bits.reinterpret_cast() |
| } |
| |
| #[doc(hidden)] |
| #[deprecated( |
| since = "1.4.0", |
| note = "use `HalfFloatSliceExt::reinterpret_cast` instead" |
| )] |
| #[inline] |
| pub fn to_bits(bits: &[f16]) -> &[u16] { |
| bits.reinterpret_cast() |
| } |
| |
| #[allow(clippy::float_cmp)] |
| #[cfg(test)] |
| mod test { |
| use super::{HalfBitsSliceExt, HalfFloatSliceExt}; |
| use crate::{bf16, f16}; |
| |
| #[test] |
| fn test_slice_conversions_f16() { |
| let bits = &[ |
| f16::E.to_bits(), |
| f16::PI.to_bits(), |
| f16::EPSILON.to_bits(), |
| f16::FRAC_1_SQRT_2.to_bits(), |
| ]; |
| let numbers = &[f16::E, f16::PI, f16::EPSILON, f16::FRAC_1_SQRT_2]; |
| |
| // Convert from bits to numbers |
| let from_bits = bits.reinterpret_cast::<f16>(); |
| assert_eq!(from_bits, numbers); |
| |
| // Convert from numbers back to bits |
| let to_bits = from_bits.reinterpret_cast(); |
| assert_eq!(to_bits, bits); |
| } |
| |
| #[test] |
| fn test_mutablility_f16() { |
| let mut bits_array = [f16::PI.to_bits()]; |
| let bits = &mut bits_array[..]; |
| |
| { |
| // would not compile without these braces |
| let numbers = bits.reinterpret_cast_mut(); |
| numbers[0] = f16::E; |
| } |
| |
| assert_eq!(bits, &[f16::E.to_bits()]); |
| |
| bits[0] = f16::LN_2.to_bits(); |
| assert_eq!(bits, &[f16::LN_2.to_bits()]); |
| } |
| |
| #[test] |
| fn test_slice_conversions_bf16() { |
| let bits = &[ |
| bf16::E.to_bits(), |
| bf16::PI.to_bits(), |
| bf16::EPSILON.to_bits(), |
| bf16::FRAC_1_SQRT_2.to_bits(), |
| ]; |
| let numbers = &[bf16::E, bf16::PI, bf16::EPSILON, bf16::FRAC_1_SQRT_2]; |
| |
| // Convert from bits to numbers |
| let from_bits = bits.reinterpret_cast::<bf16>(); |
| assert_eq!(from_bits, numbers); |
| |
| // Convert from numbers back to bits |
| let to_bits = from_bits.reinterpret_cast(); |
| assert_eq!(to_bits, bits); |
| } |
| |
| #[test] |
| fn test_mutablility_bf16() { |
| let mut bits_array = [bf16::PI.to_bits()]; |
| let bits = &mut bits_array[..]; |
| |
| { |
| // would not compile without these braces |
| let numbers = bits.reinterpret_cast_mut(); |
| numbers[0] = bf16::E; |
| } |
| |
| assert_eq!(bits, &[bf16::E.to_bits()]); |
| |
| bits[0] = bf16::LN_2.to_bits(); |
| assert_eq!(bits, &[bf16::LN_2.to_bits()]); |
| } |
| |
| #[test] |
| fn slice_convert_f16_f32() { |
| // Exact chunks |
| let vf32 = [1., 2., 3., 4., 5., 6., 7., 8.]; |
| let vf16 = [ |
| f16::from_f32(1.), |
| f16::from_f32(2.), |
| f16::from_f32(3.), |
| f16::from_f32(4.), |
| f16::from_f32(5.), |
| f16::from_f32(6.), |
| f16::from_f32(7.), |
| f16::from_f32(8.), |
| ]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf32 = [1., 2., 3., 4., 5., 6., 7., 8., 9.]; |
| let vf16 = [ |
| f16::from_f32(1.), |
| f16::from_f32(2.), |
| f16::from_f32(3.), |
| f16::from_f32(4.), |
| f16::from_f32(5.), |
| f16::from_f32(6.), |
| f16::from_f32(7.), |
| f16::from_f32(8.), |
| f16::from_f32(9.), |
| ]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf32 = [1., 2.]; |
| let vf16 = [f16::from_f32(1.), f16::from_f32(2.)]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| } |
| |
| #[test] |
| fn slice_convert_bf16_f32() { |
| // Exact chunks |
| let vf32 = [1., 2., 3., 4., 5., 6., 7., 8.]; |
| let vf16 = [ |
| bf16::from_f32(1.), |
| bf16::from_f32(2.), |
| bf16::from_f32(3.), |
| bf16::from_f32(4.), |
| bf16::from_f32(5.), |
| bf16::from_f32(6.), |
| bf16::from_f32(7.), |
| bf16::from_f32(8.), |
| ]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf32 = [1., 2., 3., 4., 5., 6., 7., 8., 9.]; |
| let vf16 = [ |
| bf16::from_f32(1.), |
| bf16::from_f32(2.), |
| bf16::from_f32(3.), |
| bf16::from_f32(4.), |
| bf16::from_f32(5.), |
| bf16::from_f32(6.), |
| bf16::from_f32(7.), |
| bf16::from_f32(8.), |
| bf16::from_f32(9.), |
| ]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf32 = [1., 2.]; |
| let vf16 = [bf16::from_f32(1.), bf16::from_f32(2.)]; |
| let mut buf32 = vf32; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f32_slice(&mut buf32); |
| assert_eq!(&vf32, &buf32); |
| |
| buf16.convert_from_f32_slice(&vf32); |
| assert_eq!(&vf16, &buf16); |
| } |
| |
| #[test] |
| fn slice_convert_f16_f64() { |
| // Exact chunks |
| let vf64 = [1., 2., 3., 4., 5., 6., 7., 8.]; |
| let vf16 = [ |
| f16::from_f64(1.), |
| f16::from_f64(2.), |
| f16::from_f64(3.), |
| f16::from_f64(4.), |
| f16::from_f64(5.), |
| f16::from_f64(6.), |
| f16::from_f64(7.), |
| f16::from_f64(8.), |
| ]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf64 = [1., 2., 3., 4., 5., 6., 7., 8., 9.]; |
| let vf16 = [ |
| f16::from_f64(1.), |
| f16::from_f64(2.), |
| f16::from_f64(3.), |
| f16::from_f64(4.), |
| f16::from_f64(5.), |
| f16::from_f64(6.), |
| f16::from_f64(7.), |
| f16::from_f64(8.), |
| f16::from_f64(9.), |
| ]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf64 = [1., 2.]; |
| let vf16 = [f16::from_f64(1.), f16::from_f64(2.)]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| } |
| |
| #[test] |
| fn slice_convert_bf16_f64() { |
| // Exact chunks |
| let vf64 = [1., 2., 3., 4., 5., 6., 7., 8.]; |
| let vf16 = [ |
| bf16::from_f64(1.), |
| bf16::from_f64(2.), |
| bf16::from_f64(3.), |
| bf16::from_f64(4.), |
| bf16::from_f64(5.), |
| bf16::from_f64(6.), |
| bf16::from_f64(7.), |
| bf16::from_f64(8.), |
| ]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf64 = [1., 2., 3., 4., 5., 6., 7., 8., 9.]; |
| let vf16 = [ |
| bf16::from_f64(1.), |
| bf16::from_f64(2.), |
| bf16::from_f64(3.), |
| bf16::from_f64(4.), |
| bf16::from_f64(5.), |
| bf16::from_f64(6.), |
| bf16::from_f64(7.), |
| bf16::from_f64(8.), |
| bf16::from_f64(9.), |
| ]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| |
| // Partial with chunks |
| let vf64 = [1., 2.]; |
| let vf16 = [bf16::from_f64(1.), bf16::from_f64(2.)]; |
| let mut buf64 = vf64; |
| let mut buf16 = vf16; |
| |
| vf16.convert_to_f64_slice(&mut buf64); |
| assert_eq!(&vf64, &buf64); |
| |
| buf16.convert_from_f64_slice(&vf64); |
| assert_eq!(&vf16, &buf16); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn convert_from_f32_slice_len_mismatch_panics() { |
| let mut slice1 = [f16::ZERO; 3]; |
| let slice2 = [0f32; 4]; |
| slice1.convert_from_f32_slice(&slice2); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn convert_from_f64_slice_len_mismatch_panics() { |
| let mut slice1 = [f16::ZERO; 3]; |
| let slice2 = [0f64; 4]; |
| slice1.convert_from_f64_slice(&slice2); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn convert_to_f32_slice_len_mismatch_panics() { |
| let slice1 = [f16::ZERO; 3]; |
| let mut slice2 = [0f32; 4]; |
| slice1.convert_to_f32_slice(&mut slice2); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn convert_to_f64_slice_len_mismatch_panics() { |
| let slice1 = [f16::ZERO; 3]; |
| let mut slice2 = [0f64; 4]; |
| slice1.convert_to_f64_slice(&mut slice2); |
| } |
| } |