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}