Skip to main content

core/num/
bfloat.rs

1//! The 16-bit brain floating-point type.
2
3#![unstable(feature = "f16b", issue = "160630")]
4
5use crate::{fmt, mem};
6
7/// A 16-bit brain floating-point value.
8///
9/// This type stores values using the bfloat16 encoding. It deliberately
10/// exposes only raw-bit construction, comparison, formatting, and lossless
11/// widening to [`f32`].
12///
13/// The 16-bit brain floating-point intends to preserve the dynamic range of
14/// a 32-bit floating-point value while using half the storage. It does
15/// this by using 8 bits for the exponent, the same as `f32`, but only
16/// using 7 bits for the mantissa. See [Wikipedia on bfloat16][wikipedia] for
17/// more information.
18///
19/// [wikipedia]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format
20#[lang = "f16b"]
21#[doc(alias = "bf16")] // what hardware often names it
22#[doc(alias = "bfloat")] // LLVM's name
23#[doc(alias = "bfloat16")] // Wikipedia's name
24#[doc(alias = "bfloat16_t")] // The C++ `stdfloat` name
25#[allow(non_camel_case_types)]
26#[repr(transparent)]
27#[unstable(feature = "f16b", issue = "160630")]
28pub struct f16b(u16);
29
30#[doc(test(attr(
31    feature(cfg_target_has_reliable_f16b),
32    allow(internal_features, unused_features)
33)))]
34impl f16b {
35    /// Raw transmutation from `u16`.
36    ///
37    /// This is currently identical to `transmute::<u16, f16b>(v)` on all platforms.
38    /// It turns out this is incredibly portable, for two reasons:
39    ///
40    /// * Floats and Ints have the same endianness on all supported platforms.
41    /// * IEEE 754 very precisely specifies the bit layout of floats.
42    ///
43    /// However there is one caveat: prior to the 2008 version of IEEE 754, how
44    /// to interpret the NaN signaling bit wasn't actually specified. Most platforms
45    /// (notably x86 and ARM) picked the interpretation that was ultimately
46    /// standardized in 2008, but some didn't (notably MIPS). As a result, all
47    /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa.
48    ///
49    /// Rather than trying to preserve signaling-ness cross-platform, this
50    /// implementation favors preserving the exact bits. This means that
51    /// any payloads encoded in NaNs will be preserved even if the result of
52    /// this method is sent over the network from an x86 machine to a MIPS one.
53    ///
54    /// If the results of this method are only manipulated by the same
55    /// architecture that produced them, then there is no portability concern.
56    ///
57    /// If the input isn't NaN, then there is no portability concern.
58    ///
59    /// If you don't care about signalingness (very likely), then there is no
60    /// portability concern.
61    ///
62    /// Note that this function is distinct from `as` casting, which attempts to
63    /// preserve the *numeric* value, and not the bitwise value.
64    ///
65    /// ```no_run
66    /// #![feature(f16b)]
67    /// # #[cfg(target_has_reliable_f16b)] {
68    /// use core::num::f16b;
69    ///
70    /// let v = f16b::from_bits(0x4148);
71    /// assert_eq!(f32::from(v), 12.5);
72    /// # }
73    /// ```
74    #[inline]
75    #[must_use]
76    #[unstable(feature = "f16b", issue = "160630")]
77    pub const fn from_bits(bits: u16) -> Self {
78        // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid.
79        unsafe { mem::transmute(bits) }
80    }
81
82    /// Raw transmutation to `u16`.
83    ///
84    /// This is currently identical to `transmute::<f16b, u16>(self)` on all platforms.
85    ///
86    /// See [`from_bits`](#method.from_bits) for some discussion of the
87    /// portability of this operation (there are almost no issues).
88    ///
89    /// Note that this function is distinct from `as` casting, which attempts to
90    /// preserve the *numeric* value, and not the bitwise value.
91    ///
92    /// ```no_run
93    /// #![feature(f16b)]
94    /// # #[cfg(target_has_reliable_f16b)] {
95    /// use core::num::f16b;
96    ///
97    /// assert_eq!(f16b::from_bits(0x4148).to_bits(), 0x4148);
98    /// # }
99    /// ```
100    #[inline]
101    #[unstable(feature = "f16b", issue = "160630")]
102    #[must_use = "this returns the result of the operation, without modifying the original"]
103    pub const fn to_bits(self) -> u16 {
104        // SAFETY: `f16b` and `u16` have the same size, and every bit pattern is valid.
105        unsafe { mem::transmute(self) }
106    }
107}
108
109// FIXME(f16b) - This should be a `#[rustc_intrinsic]` using LLVM's `fpext`
110// with this implementation constituting the fallback.
111#[inline]
112const fn widen(value: f16b) -> f32 {
113    f32::from_bits((value.to_bits() as u32) << 16)
114}
115
116#[unstable(feature = "f16b", issue = "160630")]
117impl Copy for f16b {}
118
119#[unstable(feature = "f16b", issue = "160630")]
120impl Clone for f16b {
121    #[inline]
122    fn clone(&self) -> Self {
123        *self
124    }
125}
126
127#[unstable(feature = "f16b", issue = "160630")]
128impl Default for f16b {
129    #[inline]
130    fn default() -> Self {
131        Self::from_bits(0)
132    }
133}
134
135#[unstable(feature = "f16b", issue = "160630")]
136impl PartialEq for f16b {
137    #[inline]
138    fn eq(&self, other: &Self) -> bool {
139        widen(*self).eq(&widen(*other))
140    }
141}
142
143#[unstable(feature = "f16b", issue = "160630")]
144impl PartialOrd for f16b {
145    #[inline]
146    fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
147        widen(*self).partial_cmp(&widen(*other))
148    }
149}
150
151#[unstable(feature = "f16b", issue = "160630")]
152impl From<f16b> for f32 {
153    #[inline]
154    fn from(value: f16b) -> Self {
155        widen(value)
156    }
157}
158
159#[unstable(feature = "f16b", issue = "160630")]
160impl fmt::Debug for f16b {
161    #[inline]
162    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
163        fmt::Debug::fmt(&widen(*self), formatter)
164    }
165}
166
167#[cfg(not(no_fp_fmt_parse))]
168#[unstable(feature = "f16b", issue = "160630")]
169impl fmt::LowerExp for f16b {
170    #[inline]
171    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
172        fmt::LowerExp::fmt(&widen(*self), formatter)
173    }
174}
175
176#[cfg(not(no_fp_fmt_parse))]
177#[unstable(feature = "f16b", issue = "160630")]
178impl fmt::UpperExp for f16b {
179    #[inline]
180    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
181        fmt::UpperExp::fmt(&widen(*self), formatter)
182    }
183}