1 //! Contains utility functions and traits to convert between slices of [`u16`] bits and [`f16`] or
2 //! [`bf16`] numbers.
3 //!
4 //! The utility [`HalfBitsSliceExt`] sealed extension trait is implemented for `[u16]` slices,
5 //! while the utility [`HalfFloatSliceExt`] sealed extension trait is implemented for both `[f16]`
6 //! and `[bf16]` slices. These traits provide efficient conversions and reinterpret casting of
7 //! larger buffers of floating point values, and are automatically included in the
8 //! [`prelude`][crate::prelude] module.
9 
10 use crate::{bf16, binary16::convert, f16};
11 #[cfg(feature = "alloc")]
12 use alloc::vec::Vec;
13 use core::slice;
14 
15 /// Extensions to `[f16]` and `[bf16]` slices to support conversion and reinterpret operations.
16 ///
17 /// This trait is sealed and cannot be implemented outside of this crate.
18 pub trait HalfFloatSliceExt: private::SealedHalfFloatSlice {
19     /// Reinterprets a slice of [`f16`] or [`bf16`] numbers as a slice of [`u16`] bits.
20     ///
21     /// This is a zero-copy operation. The reinterpreted slice has the same lifetime and memory
22     /// location as `self`.
23     ///
24     /// # Examples
25     ///
26     /// ```rust
27     /// # use half::prelude::*;
28     /// let float_buffer = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)];
29     /// let int_buffer = float_buffer.reinterpret_cast();
30     ///
31     /// assert_eq!(int_buffer, [float_buffer[0].to_bits(), float_buffer[1].to_bits(), float_buffer[2].to_bits()]);
32     /// ```
33     #[must_use]
reinterpret_cast(&self) -> &[u16]34     fn reinterpret_cast(&self) -> &[u16];
35 
36     /// Reinterprets a mutable slice of [`f16`] or [`bf16`] numbers as a mutable slice of [`u16`].
37     /// bits
38     ///
39     /// This is a zero-copy operation. The transmuted slice has the same lifetime as the original,
40     /// which prevents mutating `self` as long as the returned `&mut [u16]` is borrowed.
41     ///
42     /// # Examples
43     ///
44     /// ```rust
45     /// # use half::prelude::*;
46     /// let mut float_buffer = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)];
47     ///
48     /// {
49     ///     let int_buffer = float_buffer.reinterpret_cast_mut();
50     ///
51     ///     assert_eq!(int_buffer, [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]);
52     ///
53     ///     // Mutating the u16 slice will mutating the original
54     ///     int_buffer[0] = 0;
55     /// }
56     ///
57     /// // Note that we need to drop int_buffer before using float_buffer again or we will get a borrow error.
58     /// assert_eq!(float_buffer, [f16::from_f32(0.), f16::from_f32(2.), f16::from_f32(3.)]);
59     /// ```
60     #[must_use]
reinterpret_cast_mut(&mut self) -> &mut [u16]61     fn reinterpret_cast_mut(&mut self) -> &mut [u16];
62 
63     /// Converts all of the elements of a `[f32]` slice into [`f16`] or [`bf16`] values in `self`.
64     ///
65     /// The length of `src` must be the same as `self`.
66     ///
67     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
68     /// efficient than converting individual elements on some hardware that supports SIMD
69     /// conversions. See [crate documentation](crate) for more information on hardware conversion
70     /// support.
71     ///
72     /// # Panics
73     ///
74     /// This function will panic if the two slices have different lengths.
75     ///
76     /// # Examples
77     /// ```rust
78     /// # use half::prelude::*;
79     /// // Initialize an empty buffer
80     /// let mut buffer = [0u16; 4];
81     /// let buffer = buffer.reinterpret_cast_mut::<f16>();
82     ///
83     /// let float_values = [1., 2., 3., 4.];
84     ///
85     /// // Now convert
86     /// buffer.convert_from_f32_slice(&float_values);
87     ///
88     /// assert_eq!(buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)]);
89     /// ```
convert_from_f32_slice(&mut self, src: &[f32])90     fn convert_from_f32_slice(&mut self, src: &[f32]);
91 
92     /// Converts all of the elements of a `[f64]` slice into [`f16`] or [`bf16`] values in `self`.
93     ///
94     /// The length of `src` must be the same as `self`.
95     ///
96     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
97     /// efficient than converting individual elements on some hardware that supports SIMD
98     /// conversions. See [crate documentation](crate) for more information on hardware conversion
99     /// support.
100     ///
101     /// # Panics
102     ///
103     /// This function will panic if the two slices have different lengths.
104     ///
105     /// # Examples
106     /// ```rust
107     /// # use half::prelude::*;
108     /// // Initialize an empty buffer
109     /// let mut buffer = [0u16; 4];
110     /// let buffer = buffer.reinterpret_cast_mut::<f16>();
111     ///
112     /// let float_values = [1., 2., 3., 4.];
113     ///
114     /// // Now convert
115     /// buffer.convert_from_f64_slice(&float_values);
116     ///
117     /// assert_eq!(buffer, [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)]);
118     /// ```
convert_from_f64_slice(&mut self, src: &[f64])119     fn convert_from_f64_slice(&mut self, src: &[f64]);
120 
121     /// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f32`] values in `dst`.
122     ///
123     /// The length of `src` must be the same as `self`.
124     ///
125     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
126     /// efficient than converting individual elements on some hardware that supports SIMD
127     /// conversions. See [crate documentation](crate) for more information on hardware conversion
128     /// support.
129     ///
130     /// # Panics
131     ///
132     /// This function will panic if the two slices have different lengths.
133     ///
134     /// # Examples
135     /// ```rust
136     /// # use half::prelude::*;
137     /// // Initialize an empty buffer
138     /// let mut buffer = [0f32; 4];
139     ///
140     /// let half_values = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)];
141     ///
142     /// // Now convert
143     /// half_values.convert_to_f32_slice(&mut buffer);
144     ///
145     /// assert_eq!(buffer, [1., 2., 3., 4.]);
146     /// ```
convert_to_f32_slice(&self, dst: &mut [f32])147     fn convert_to_f32_slice(&self, dst: &mut [f32]);
148 
149     /// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f64`] values in `dst`.
150     ///
151     /// The length of `src` must be the same as `self`.
152     ///
153     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
154     /// efficient than converting individual elements on some hardware that supports SIMD
155     /// conversions. See [crate documentation](crate) for more information on hardware conversion
156     /// support.
157     ///
158     /// # Panics
159     ///
160     /// This function will panic if the two slices have different lengths.
161     ///
162     /// # Examples
163     /// ```rust
164     /// # use half::prelude::*;
165     /// // Initialize an empty buffer
166     /// let mut buffer = [0f64; 4];
167     ///
168     /// let half_values = [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)];
169     ///
170     /// // Now convert
171     /// half_values.convert_to_f64_slice(&mut buffer);
172     ///
173     /// assert_eq!(buffer, [1., 2., 3., 4.]);
174     /// ```
convert_to_f64_slice(&self, dst: &mut [f64])175     fn convert_to_f64_slice(&self, dst: &mut [f64]);
176 
177     // Because trait is sealed, we can get away with different interfaces between features.
178 
179     /// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f32`] values in a new
180     /// vector
181     ///
182     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
183     /// efficient than converting individual elements on some hardware that supports SIMD
184     /// conversions. See [crate documentation](crate) for more information on hardware conversion
185     /// support.
186     ///
187     /// This method is only available with the `std` or `alloc` feature.
188     ///
189     /// # Examples
190     /// ```rust
191     /// # use half::prelude::*;
192     /// let half_values = [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.), f16::from_f32(4.)];
193     /// let vec = half_values.to_f32_vec();
194     ///
195     /// assert_eq!(vec, vec![1., 2., 3., 4.]);
196     /// ```
197     #[cfg(any(feature = "alloc", feature = "std"))]
198     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
199     #[must_use]
to_f32_vec(&self) -> Vec<f32>200     fn to_f32_vec(&self) -> Vec<f32>;
201 
202     /// Converts all of the [`f16`] or [`bf16`] elements of `self` into [`f64`] values in a new
203     /// vector.
204     ///
205     /// The conversion operation is vectorized over the slice, meaning the conversion may be more
206     /// efficient than converting individual elements on some hardware that supports SIMD
207     /// conversions. See [crate documentation](crate) for more information on hardware conversion
208     /// support.
209     ///
210     /// This method is only available with the `std` or `alloc` feature.
211     ///
212     /// # Examples
213     /// ```rust
214     /// # use half::prelude::*;
215     /// let half_values = [f16::from_f64(1.), f16::from_f64(2.), f16::from_f64(3.), f16::from_f64(4.)];
216     /// let vec = half_values.to_f64_vec();
217     ///
218     /// assert_eq!(vec, vec![1., 2., 3., 4.]);
219     /// ```
220     #[cfg(feature = "alloc")]
221     #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
222     #[must_use]
to_f64_vec(&self) -> Vec<f64>223     fn to_f64_vec(&self) -> Vec<f64>;
224 }
225 
226 /// Extensions to `[u16]` slices to support reinterpret operations.
227 ///
228 /// This trait is sealed and cannot be implemented outside of this crate.
229 pub trait HalfBitsSliceExt: private::SealedHalfBitsSlice {
230     /// Reinterprets a slice of [`u16`] bits as a slice of [`f16`] or [`bf16`] numbers.
231     ///
232     /// `H` is the type to cast to, and must be either the [`f16`] or [`bf16`] type.
233     ///
234     /// This is a zero-copy operation. The reinterpreted slice has the same lifetime and memory
235     /// location as `self`.
236     ///
237     /// # Examples
238     ///
239     /// ```rust
240     /// # use half::prelude::*;
241     /// let int_buffer = [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()];
242     /// let float_buffer: &[f16] = int_buffer.reinterpret_cast();
243     ///
244     /// assert_eq!(float_buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]);
245     ///
246     /// // You may have to specify the cast type directly if the compiler can't infer the type.
247     /// // The following is also valid in Rust.
248     /// let typed_buffer = int_buffer.reinterpret_cast::<f16>();
249     /// ```
250     #[must_use]
reinterpret_cast<H>(&self) -> &[H] where H: crate::private::SealedHalf251     fn reinterpret_cast<H>(&self) -> &[H]
252     where
253         H: crate::private::SealedHalf;
254 
255     /// Reinterprets a mutable slice of [`u16`] bits as a mutable slice of [`f16`] or [`bf16`]
256     /// numbers.
257     ///
258     /// `H` is the type to cast to, and must be either the [`f16`] or [`bf16`] type.
259     ///
260     /// This is a zero-copy operation. The transmuted slice has the same lifetime as the original,
261     /// which prevents mutating `self` as long as the returned `&mut [f16]` is borrowed.
262     ///
263     /// # Examples
264     ///
265     /// ```rust
266     /// # use half::prelude::*;
267     /// let mut int_buffer = [f16::from_f32(1.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()];
268     ///
269     /// {
270     ///     let float_buffer: &mut [f16] = int_buffer.reinterpret_cast_mut();
271     ///
272     ///     assert_eq!(float_buffer, [f16::from_f32(1.), f16::from_f32(2.), f16::from_f32(3.)]);
273     ///
274     ///     // Mutating the f16 slice will mutating the original
275     ///     float_buffer[0] = f16::from_f32(0.);
276     /// }
277     ///
278     /// // Note that we need to drop float_buffer before using int_buffer again or we will get a borrow error.
279     /// assert_eq!(int_buffer, [f16::from_f32(0.).to_bits(), f16::from_f32(2.).to_bits(), f16::from_f32(3.).to_bits()]);
280     ///
281     /// // You may have to specify the cast type directly if the compiler can't infer the type.
282     /// // The following is also valid in Rust.
283     /// let typed_buffer = int_buffer.reinterpret_cast_mut::<f16>();
284     /// ```
285     #[must_use]
reinterpret_cast_mut<H>(&mut self) -> &mut [H] where H: crate::private::SealedHalf286     fn reinterpret_cast_mut<H>(&mut self) -> &mut [H]
287     where
288         H: crate::private::SealedHalf;
289 }
290 
291 mod private {
292     use crate::{bf16, f16};
293 
294     pub trait SealedHalfFloatSlice {}
295     impl SealedHalfFloatSlice for [f16] {}
296     impl SealedHalfFloatSlice for [bf16] {}
297 
298     pub trait SealedHalfBitsSlice {}
299     impl SealedHalfBitsSlice for [u16] {}
300 }
301 
302 impl HalfFloatSliceExt for [f16] {
303     #[inline]
reinterpret_cast(&self) -> &[u16]304     fn reinterpret_cast(&self) -> &[u16] {
305         let pointer = self.as_ptr() as *const u16;
306         let length = self.len();
307         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
308         // and the size of elements are identical
309         unsafe { slice::from_raw_parts(pointer, length) }
310     }
311 
312     #[inline]
reinterpret_cast_mut(&mut self) -> &mut [u16]313     fn reinterpret_cast_mut(&mut self) -> &mut [u16] {
314         let pointer = self.as_mut_ptr().cast::<u16>();
315         let length = self.len();
316         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
317         // and the size of elements are identical
318         unsafe { slice::from_raw_parts_mut(pointer, length) }
319     }
320 
convert_from_f32_slice(&mut self, src: &[f32])321     fn convert_from_f32_slice(&mut self, src: &[f32]) {
322         assert_eq!(
323             self.len(),
324             src.len(),
325             "destination and source slices have different lengths"
326         );
327 
328         convert::f32_to_f16_slice(src, self.reinterpret_cast_mut())
329     }
330 
convert_from_f64_slice(&mut self, src: &[f64])331     fn convert_from_f64_slice(&mut self, src: &[f64]) {
332         assert_eq!(
333             self.len(),
334             src.len(),
335             "destination and source slices have different lengths"
336         );
337 
338         convert::f64_to_f16_slice(src, self.reinterpret_cast_mut())
339     }
340 
convert_to_f32_slice(&self, dst: &mut [f32])341     fn convert_to_f32_slice(&self, dst: &mut [f32]) {
342         assert_eq!(
343             self.len(),
344             dst.len(),
345             "destination and source slices have different lengths"
346         );
347 
348         convert::f16_to_f32_slice(self.reinterpret_cast(), dst)
349     }
350 
convert_to_f64_slice(&self, dst: &mut [f64])351     fn convert_to_f64_slice(&self, dst: &mut [f64]) {
352         assert_eq!(
353             self.len(),
354             dst.len(),
355             "destination and source slices have different lengths"
356         );
357 
358         convert::f16_to_f64_slice(self.reinterpret_cast(), dst)
359     }
360 
361     #[cfg(any(feature = "alloc", feature = "std"))]
362     #[inline]
363     #[allow(clippy::uninit_vec)]
to_f32_vec(&self) -> Vec<f32>364     fn to_f32_vec(&self) -> Vec<f32> {
365         let mut vec = Vec::with_capacity(self.len());
366         // SAFETY: convert will initialize every value in the vector without reading them,
367         // so this is safe to do instead of double initialize from resize, and we're setting it to
368         // same value as capacity.
369         unsafe { vec.set_len(self.len()) };
370         self.convert_to_f32_slice(&mut vec);
371         vec
372     }
373 
374     #[cfg(any(feature = "alloc", feature = "std"))]
375     #[inline]
376     #[allow(clippy::uninit_vec)]
to_f64_vec(&self) -> Vec<f64>377     fn to_f64_vec(&self) -> Vec<f64> {
378         let mut vec = Vec::with_capacity(self.len());
379         // SAFETY: convert will initialize every value in the vector without reading them,
380         // so this is safe to do instead of double initialize from resize, and we're setting it to
381         // same value as capacity.
382         unsafe { vec.set_len(self.len()) };
383         self.convert_to_f64_slice(&mut vec);
384         vec
385     }
386 }
387 
388 impl HalfFloatSliceExt for [bf16] {
389     #[inline]
reinterpret_cast(&self) -> &[u16]390     fn reinterpret_cast(&self) -> &[u16] {
391         let pointer = self.as_ptr() as *const u16;
392         let length = self.len();
393         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
394         // and the size of elements are identical
395         unsafe { slice::from_raw_parts(pointer, length) }
396     }
397 
398     #[inline]
reinterpret_cast_mut(&mut self) -> &mut [u16]399     fn reinterpret_cast_mut(&mut self) -> &mut [u16] {
400         let pointer = self.as_mut_ptr().cast::<u16>();
401         let length = self.len();
402         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
403         // and the size of elements are identical
404         unsafe { slice::from_raw_parts_mut(pointer, length) }
405     }
406 
convert_from_f32_slice(&mut self, src: &[f32])407     fn convert_from_f32_slice(&mut self, src: &[f32]) {
408         assert_eq!(
409             self.len(),
410             src.len(),
411             "destination and source slices have different lengths"
412         );
413 
414         // Just use regular loop here until there's any bf16 SIMD support.
415         for (i, f) in src.iter().enumerate() {
416             self[i] = bf16::from_f32(*f);
417         }
418     }
419 
convert_from_f64_slice(&mut self, src: &[f64])420     fn convert_from_f64_slice(&mut self, src: &[f64]) {
421         assert_eq!(
422             self.len(),
423             src.len(),
424             "destination and source slices have different lengths"
425         );
426 
427         // Just use regular loop here until there's any bf16 SIMD support.
428         for (i, f) in src.iter().enumerate() {
429             self[i] = bf16::from_f64(*f);
430         }
431     }
432 
convert_to_f32_slice(&self, dst: &mut [f32])433     fn convert_to_f32_slice(&self, dst: &mut [f32]) {
434         assert_eq!(
435             self.len(),
436             dst.len(),
437             "destination and source slices have different lengths"
438         );
439 
440         // Just use regular loop here until there's any bf16 SIMD support.
441         for (i, f) in self.iter().enumerate() {
442             dst[i] = f.to_f32();
443         }
444     }
445 
convert_to_f64_slice(&self, dst: &mut [f64])446     fn convert_to_f64_slice(&self, dst: &mut [f64]) {
447         assert_eq!(
448             self.len(),
449             dst.len(),
450             "destination and source slices have different lengths"
451         );
452 
453         // Just use regular loop here until there's any bf16 SIMD support.
454         for (i, f) in self.iter().enumerate() {
455             dst[i] = f.to_f64();
456         }
457     }
458 
459     #[cfg(any(feature = "alloc", feature = "std"))]
460     #[inline]
461     #[allow(clippy::uninit_vec)]
to_f32_vec(&self) -> Vec<f32>462     fn to_f32_vec(&self) -> Vec<f32> {
463         let mut vec = Vec::with_capacity(self.len());
464         // SAFETY: convert will initialize every value in the vector without reading them,
465         // so this is safe to do instead of double initialize from resize, and we're setting it to
466         // same value as capacity.
467         unsafe { vec.set_len(self.len()) };
468         self.convert_to_f32_slice(&mut vec);
469         vec
470     }
471 
472     #[cfg(any(feature = "alloc", feature = "std"))]
473     #[inline]
474     #[allow(clippy::uninit_vec)]
to_f64_vec(&self) -> Vec<f64>475     fn to_f64_vec(&self) -> Vec<f64> {
476         let mut vec = Vec::with_capacity(self.len());
477         // SAFETY: convert will initialize every value in the vector without reading them,
478         // so this is safe to do instead of double initialize from resize, and we're setting it to
479         // same value as capacity.
480         unsafe { vec.set_len(self.len()) };
481         self.convert_to_f64_slice(&mut vec);
482         vec
483     }
484 }
485 
486 impl HalfBitsSliceExt for [u16] {
487     // Since we sealed all the traits involved, these are safe.
488     #[inline]
reinterpret_cast<H>(&self) -> &[H] where H: crate::private::SealedHalf,489     fn reinterpret_cast<H>(&self) -> &[H]
490     where
491         H: crate::private::SealedHalf,
492     {
493         let pointer = self.as_ptr() as *const H;
494         let length = self.len();
495         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
496         // and the size of elements are identical
497         unsafe { slice::from_raw_parts(pointer, length) }
498     }
499 
500     #[inline]
reinterpret_cast_mut<H>(&mut self) -> &mut [H] where H: crate::private::SealedHalf,501     fn reinterpret_cast_mut<H>(&mut self) -> &mut [H]
502     where
503         H: crate::private::SealedHalf,
504     {
505         let pointer = self.as_mut_ptr() as *mut H;
506         let length = self.len();
507         // SAFETY: We are reconstructing full length of original slice, using its same lifetime,
508         // and the size of elements are identical
509         unsafe { slice::from_raw_parts_mut(pointer, length) }
510     }
511 }
512 
513 #[allow(clippy::float_cmp)]
514 #[cfg(test)]
515 mod test {
516     use super::{HalfBitsSliceExt, HalfFloatSliceExt};
517     use crate::{bf16, f16};
518 
519     #[test]
test_slice_conversions_f16()520     fn test_slice_conversions_f16() {
521         let bits = &[
522             f16::E.to_bits(),
523             f16::PI.to_bits(),
524             f16::EPSILON.to_bits(),
525             f16::FRAC_1_SQRT_2.to_bits(),
526         ];
527         let numbers = &[f16::E, f16::PI, f16::EPSILON, f16::FRAC_1_SQRT_2];
528 
529         // Convert from bits to numbers
530         let from_bits = bits.reinterpret_cast::<f16>();
531         assert_eq!(from_bits, numbers);
532 
533         // Convert from numbers back to bits
534         let to_bits = from_bits.reinterpret_cast();
535         assert_eq!(to_bits, bits);
536     }
537 
538     #[test]
test_mutablility_f16()539     fn test_mutablility_f16() {
540         let mut bits_array = [f16::PI.to_bits()];
541         let bits = &mut bits_array[..];
542 
543         {
544             // would not compile without these braces
545             let numbers = bits.reinterpret_cast_mut();
546             numbers[0] = f16::E;
547         }
548 
549         assert_eq!(bits, &[f16::E.to_bits()]);
550 
551         bits[0] = f16::LN_2.to_bits();
552         assert_eq!(bits, &[f16::LN_2.to_bits()]);
553     }
554 
555     #[test]
test_slice_conversions_bf16()556     fn test_slice_conversions_bf16() {
557         let bits = &[
558             bf16::E.to_bits(),
559             bf16::PI.to_bits(),
560             bf16::EPSILON.to_bits(),
561             bf16::FRAC_1_SQRT_2.to_bits(),
562         ];
563         let numbers = &[bf16::E, bf16::PI, bf16::EPSILON, bf16::FRAC_1_SQRT_2];
564 
565         // Convert from bits to numbers
566         let from_bits = bits.reinterpret_cast::<bf16>();
567         assert_eq!(from_bits, numbers);
568 
569         // Convert from numbers back to bits
570         let to_bits = from_bits.reinterpret_cast();
571         assert_eq!(to_bits, bits);
572     }
573 
574     #[test]
test_mutablility_bf16()575     fn test_mutablility_bf16() {
576         let mut bits_array = [bf16::PI.to_bits()];
577         let bits = &mut bits_array[..];
578 
579         {
580             // would not compile without these braces
581             let numbers = bits.reinterpret_cast_mut();
582             numbers[0] = bf16::E;
583         }
584 
585         assert_eq!(bits, &[bf16::E.to_bits()]);
586 
587         bits[0] = bf16::LN_2.to_bits();
588         assert_eq!(bits, &[bf16::LN_2.to_bits()]);
589     }
590 
591     #[test]
slice_convert_f16_f32()592     fn slice_convert_f16_f32() {
593         // Exact chunks
594         let vf32 = [1., 2., 3., 4., 5., 6., 7., 8.];
595         let vf16 = [
596             f16::from_f32(1.),
597             f16::from_f32(2.),
598             f16::from_f32(3.),
599             f16::from_f32(4.),
600             f16::from_f32(5.),
601             f16::from_f32(6.),
602             f16::from_f32(7.),
603             f16::from_f32(8.),
604         ];
605         let mut buf32 = vf32;
606         let mut buf16 = vf16;
607 
608         vf16.convert_to_f32_slice(&mut buf32);
609         assert_eq!(&vf32, &buf32);
610 
611         buf16.convert_from_f32_slice(&vf32);
612         assert_eq!(&vf16, &buf16);
613 
614         // Partial with chunks
615         let vf32 = [1., 2., 3., 4., 5., 6., 7., 8., 9.];
616         let vf16 = [
617             f16::from_f32(1.),
618             f16::from_f32(2.),
619             f16::from_f32(3.),
620             f16::from_f32(4.),
621             f16::from_f32(5.),
622             f16::from_f32(6.),
623             f16::from_f32(7.),
624             f16::from_f32(8.),
625             f16::from_f32(9.),
626         ];
627         let mut buf32 = vf32;
628         let mut buf16 = vf16;
629 
630         vf16.convert_to_f32_slice(&mut buf32);
631         assert_eq!(&vf32, &buf32);
632 
633         buf16.convert_from_f32_slice(&vf32);
634         assert_eq!(&vf16, &buf16);
635 
636         // Partial with chunks
637         let vf32 = [1., 2.];
638         let vf16 = [f16::from_f32(1.), f16::from_f32(2.)];
639         let mut buf32 = vf32;
640         let mut buf16 = vf16;
641 
642         vf16.convert_to_f32_slice(&mut buf32);
643         assert_eq!(&vf32, &buf32);
644 
645         buf16.convert_from_f32_slice(&vf32);
646         assert_eq!(&vf16, &buf16);
647     }
648 
649     #[test]
slice_convert_bf16_f32()650     fn slice_convert_bf16_f32() {
651         // Exact chunks
652         let vf32 = [1., 2., 3., 4., 5., 6., 7., 8.];
653         let vf16 = [
654             bf16::from_f32(1.),
655             bf16::from_f32(2.),
656             bf16::from_f32(3.),
657             bf16::from_f32(4.),
658             bf16::from_f32(5.),
659             bf16::from_f32(6.),
660             bf16::from_f32(7.),
661             bf16::from_f32(8.),
662         ];
663         let mut buf32 = vf32;
664         let mut buf16 = vf16;
665 
666         vf16.convert_to_f32_slice(&mut buf32);
667         assert_eq!(&vf32, &buf32);
668 
669         buf16.convert_from_f32_slice(&vf32);
670         assert_eq!(&vf16, &buf16);
671 
672         // Partial with chunks
673         let vf32 = [1., 2., 3., 4., 5., 6., 7., 8., 9.];
674         let vf16 = [
675             bf16::from_f32(1.),
676             bf16::from_f32(2.),
677             bf16::from_f32(3.),
678             bf16::from_f32(4.),
679             bf16::from_f32(5.),
680             bf16::from_f32(6.),
681             bf16::from_f32(7.),
682             bf16::from_f32(8.),
683             bf16::from_f32(9.),
684         ];
685         let mut buf32 = vf32;
686         let mut buf16 = vf16;
687 
688         vf16.convert_to_f32_slice(&mut buf32);
689         assert_eq!(&vf32, &buf32);
690 
691         buf16.convert_from_f32_slice(&vf32);
692         assert_eq!(&vf16, &buf16);
693 
694         // Partial with chunks
695         let vf32 = [1., 2.];
696         let vf16 = [bf16::from_f32(1.), bf16::from_f32(2.)];
697         let mut buf32 = vf32;
698         let mut buf16 = vf16;
699 
700         vf16.convert_to_f32_slice(&mut buf32);
701         assert_eq!(&vf32, &buf32);
702 
703         buf16.convert_from_f32_slice(&vf32);
704         assert_eq!(&vf16, &buf16);
705     }
706 
707     #[test]
slice_convert_f16_f64()708     fn slice_convert_f16_f64() {
709         // Exact chunks
710         let vf64 = [1., 2., 3., 4., 5., 6., 7., 8.];
711         let vf16 = [
712             f16::from_f64(1.),
713             f16::from_f64(2.),
714             f16::from_f64(3.),
715             f16::from_f64(4.),
716             f16::from_f64(5.),
717             f16::from_f64(6.),
718             f16::from_f64(7.),
719             f16::from_f64(8.),
720         ];
721         let mut buf64 = vf64;
722         let mut buf16 = vf16;
723 
724         vf16.convert_to_f64_slice(&mut buf64);
725         assert_eq!(&vf64, &buf64);
726 
727         buf16.convert_from_f64_slice(&vf64);
728         assert_eq!(&vf16, &buf16);
729 
730         // Partial with chunks
731         let vf64 = [1., 2., 3., 4., 5., 6., 7., 8., 9.];
732         let vf16 = [
733             f16::from_f64(1.),
734             f16::from_f64(2.),
735             f16::from_f64(3.),
736             f16::from_f64(4.),
737             f16::from_f64(5.),
738             f16::from_f64(6.),
739             f16::from_f64(7.),
740             f16::from_f64(8.),
741             f16::from_f64(9.),
742         ];
743         let mut buf64 = vf64;
744         let mut buf16 = vf16;
745 
746         vf16.convert_to_f64_slice(&mut buf64);
747         assert_eq!(&vf64, &buf64);
748 
749         buf16.convert_from_f64_slice(&vf64);
750         assert_eq!(&vf16, &buf16);
751 
752         // Partial with chunks
753         let vf64 = [1., 2.];
754         let vf16 = [f16::from_f64(1.), f16::from_f64(2.)];
755         let mut buf64 = vf64;
756         let mut buf16 = vf16;
757 
758         vf16.convert_to_f64_slice(&mut buf64);
759         assert_eq!(&vf64, &buf64);
760 
761         buf16.convert_from_f64_slice(&vf64);
762         assert_eq!(&vf16, &buf16);
763     }
764 
765     #[test]
slice_convert_bf16_f64()766     fn slice_convert_bf16_f64() {
767         // Exact chunks
768         let vf64 = [1., 2., 3., 4., 5., 6., 7., 8.];
769         let vf16 = [
770             bf16::from_f64(1.),
771             bf16::from_f64(2.),
772             bf16::from_f64(3.),
773             bf16::from_f64(4.),
774             bf16::from_f64(5.),
775             bf16::from_f64(6.),
776             bf16::from_f64(7.),
777             bf16::from_f64(8.),
778         ];
779         let mut buf64 = vf64;
780         let mut buf16 = vf16;
781 
782         vf16.convert_to_f64_slice(&mut buf64);
783         assert_eq!(&vf64, &buf64);
784 
785         buf16.convert_from_f64_slice(&vf64);
786         assert_eq!(&vf16, &buf16);
787 
788         // Partial with chunks
789         let vf64 = [1., 2., 3., 4., 5., 6., 7., 8., 9.];
790         let vf16 = [
791             bf16::from_f64(1.),
792             bf16::from_f64(2.),
793             bf16::from_f64(3.),
794             bf16::from_f64(4.),
795             bf16::from_f64(5.),
796             bf16::from_f64(6.),
797             bf16::from_f64(7.),
798             bf16::from_f64(8.),
799             bf16::from_f64(9.),
800         ];
801         let mut buf64 = vf64;
802         let mut buf16 = vf16;
803 
804         vf16.convert_to_f64_slice(&mut buf64);
805         assert_eq!(&vf64, &buf64);
806 
807         buf16.convert_from_f64_slice(&vf64);
808         assert_eq!(&vf16, &buf16);
809 
810         // Partial with chunks
811         let vf64 = [1., 2.];
812         let vf16 = [bf16::from_f64(1.), bf16::from_f64(2.)];
813         let mut buf64 = vf64;
814         let mut buf16 = vf16;
815 
816         vf16.convert_to_f64_slice(&mut buf64);
817         assert_eq!(&vf64, &buf64);
818 
819         buf16.convert_from_f64_slice(&vf64);
820         assert_eq!(&vf16, &buf16);
821     }
822 
823     #[test]
824     #[should_panic]
convert_from_f32_slice_len_mismatch_panics()825     fn convert_from_f32_slice_len_mismatch_panics() {
826         let mut slice1 = [f16::ZERO; 3];
827         let slice2 = [0f32; 4];
828         slice1.convert_from_f32_slice(&slice2);
829     }
830 
831     #[test]
832     #[should_panic]
convert_from_f64_slice_len_mismatch_panics()833     fn convert_from_f64_slice_len_mismatch_panics() {
834         let mut slice1 = [f16::ZERO; 3];
835         let slice2 = [0f64; 4];
836         slice1.convert_from_f64_slice(&slice2);
837     }
838 
839     #[test]
840     #[should_panic]
convert_to_f32_slice_len_mismatch_panics()841     fn convert_to_f32_slice_len_mismatch_panics() {
842         let slice1 = [f16::ZERO; 3];
843         let mut slice2 = [0f32; 4];
844         slice1.convert_to_f32_slice(&mut slice2);
845     }
846 
847     #[test]
848     #[should_panic]
convert_to_f64_slice_len_mismatch_panics()849     fn convert_to_f64_slice_len_mismatch_panics() {
850         let slice1 = [f16::ZERO; 3];
851         let mut slice2 = [0f64; 4];
852         slice1.convert_to_f64_slice(&mut slice2);
853     }
854 }
855