//! 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()]); /// ``` #[must_use] 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.)]); /// ``` #[must_use] 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::(); /// /// 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::(); /// /// 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")))] #[must_use] fn to_f32_vec(&self) -> Vec; /// 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")))] #[must_use] fn to_f64_vec(&self) -> Vec; } /// 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::(); /// ``` #[must_use] fn reinterpret_cast(&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::(); /// ``` #[must_use] fn reinterpret_cast_mut(&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_mut_ptr().cast::(); 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" ); convert::f32_to_f16_slice(src, self.reinterpret_cast_mut()) } fn convert_from_f64_slice(&mut self, src: &[f64]) { assert_eq!( self.len(), src.len(), "destination and source slices have different lengths" ); convert::f64_to_f16_slice(src, self.reinterpret_cast_mut()) } fn convert_to_f32_slice(&self, dst: &mut [f32]) { assert_eq!( self.len(), dst.len(), "destination and source slices have different lengths" ); convert::f16_to_f32_slice(self.reinterpret_cast(), dst) } fn convert_to_f64_slice(&self, dst: &mut [f64]) { assert_eq!( self.len(), dst.len(), "destination and source slices have different lengths" ); convert::f16_to_f64_slice(self.reinterpret_cast(), dst) } #[cfg(any(feature = "alloc", feature = "std"))] #[inline] #[allow(clippy::uninit_vec)] fn to_f32_vec(&self) -> Vec { 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] #[allow(clippy::uninit_vec)] fn to_f64_vec(&self) -> Vec { 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_mut_ptr().cast::(); 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] #[allow(clippy::uninit_vec)] fn to_f32_vec(&self) -> Vec { 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] #[allow(clippy::uninit_vec)] fn to_f64_vec(&self) -> Vec { 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(&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(&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) } } } #[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::(); 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::(); 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); } }