855 lines
28 KiB
Rust
855 lines
28 KiB
Rust
//! 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::<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")))]
|
|
#[must_use]
|
|
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")))]
|
|
#[must_use]
|
|
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>();
|
|
/// ```
|
|
#[must_use]
|
|
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>();
|
|
/// ```
|
|
#[must_use]
|
|
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_mut_ptr().cast::<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"
|
|
);
|
|
|
|
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<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]
|
|
#[allow(clippy::uninit_vec)]
|
|
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_mut_ptr().cast::<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]
|
|
#[allow(clippy::uninit_vec)]
|
|
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]
|
|
#[allow(clippy::uninit_vec)]
|
|
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) }
|
|
}
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|