#![cfg(feature = "f16")]
#![doc(hidden)]
use core::cmp::Ordering;
use core::iter::{Product, Sum};
use core::num::FpCategory;
use core::ops::*;
use core::{fmt, mem, num, str};
use crate::num::Float;
use crate::numtypes::*;
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Default, Copy, Clone)]
pub struct bf16(u16);
unsafe impl Send for bf16 {
}
unsafe impl Sync for bf16 {
}
impl bf16 {
pub const RADIX: u32 = 2;
pub const MANTISSA_DIGITS: u32 = <Self as Float>::MANTISSA_SIZE as u32 + 1;
pub const DIGITS: u32 = 2;
pub const EPSILON: Self = bf16(0x3C00u16);
pub const MIN: Self = <Self as Float>::MIN;
pub const MIN_POSITIVE: Self = Self(0x0080u16);
pub const MAX: Self = <Self as Float>::MAX;
pub const MAX_EXP: i32 = Self::MAX_EXPONENT + Self::MANTISSA_SIZE;
pub const MIN_EXP: i32 = -Self::MAX_EXP + 3;
pub const MIN_10_EXP: i32 = -37;
pub const MAX_10_EXP: i32 = 38;
pub const NAN: Self = <Self as Float>::NAN;
pub const INFINITY: Self = <Self as Float>::INFINITY;
pub const NEG_INFINITY: Self = <Self as Float>::NEG_INFINITY;
pub const SIGN_MASK: u16 = <Self as Float>::SIGN_MASK;
pub const EXP_MASK: u16 = <Self as Float>::EXPONENT_MASK;
pub const MAN_MASK: u16 = <Self as Float>::MANTISSA_MASK;
pub const TINY_BITS: u16 = 0x1;
pub const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK;
#[must_use]
#[inline(always)]
pub const fn is_nan(self) -> bool {
let bits = self.to_bits();
let is_special = bits & Self::EXPONENT_MASK == Self::EXPONENT_MASK;
is_special && (bits & Self::MANTISSA_MASK) != 0
}
#[must_use]
#[inline(always)]
pub const fn abs(self) -> Self {
Self(self.0 & !Self::SIGN_MASK)
}
#[must_use]
#[inline(always)]
pub const fn is_infinite(self) -> bool {
eq(self, Self::INFINITY) | eq(self, Self::NEG_INFINITY)
}
#[must_use]
#[inline(always)]
pub const fn is_finite(self) -> bool {
self.to_bits() & Self::EXPONENT_MASK != Self::EXPONENT_MASK
}
#[must_use]
#[inline(always)]
pub const fn is_subnormal(self) -> bool {
matches!(self.classify(), FpCategory::Subnormal)
}
#[must_use]
#[inline(always)]
pub const fn is_normal(self) -> bool {
matches!(self.classify(), FpCategory::Normal)
}
#[inline(always)]
pub const fn classify(self) -> FpCategory {
let b = self.to_bits();
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
(0, Self::EXP_MASK) => FpCategory::Infinite,
(_, Self::EXP_MASK) => FpCategory::Nan,
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
_ => FpCategory::Normal,
}
}
#[inline(always)]
pub const fn is_sign_positive(self) -> bool {
self.to_bits() & Self::SIGN_MASK == 0
}
#[inline(always)]
pub const fn is_sign_negative(self) -> bool {
!self.is_sign_positive()
}
#[must_use]
#[inline(always)]
pub fn recip(self) -> Self {
Self::ONE / self
}
#[must_use]
#[inline(always)]
pub fn to_degrees(self) -> Self {
self * Self::from_u16(180) / Self::PI
}
#[must_use]
#[inline(always)]
pub fn to_radians(self) -> Self {
self * Self::PI / Self::from_u16(180)
}
#[must_use]
#[inline(always)]
pub fn max(self, other: Self) -> Self {
if other > self && !other.is_nan() {
other
} else {
self
}
}
#[must_use]
#[inline(always)]
pub fn min(self, other: Self) -> Self {
if other < self && !other.is_nan() {
other
} else {
self
}
}
#[must_use]
#[inline(always)]
pub const fn to_bits(self) -> u16 {
self.0
}
#[must_use]
#[inline(always)]
pub const fn from_bits(bits: u16) -> Self {
Self(bits)
}
#[must_use]
#[inline(always)]
pub const fn to_be_bytes(self) -> [u8; 2] {
self.to_bits().to_be_bytes()
}
#[must_use]
#[inline(always)]
pub const fn to_le_bytes(self) -> [u8; 2] {
self.to_bits().to_le_bytes()
}
#[must_use]
#[inline(always)]
pub const fn to_ne_bytes(self) -> [u8; 2] {
self.to_bits().to_ne_bytes()
}
#[must_use]
#[inline(always)]
pub const fn from_be_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_be_bytes(bytes))
}
#[must_use]
#[inline(always)]
pub const fn from_le_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_le_bytes(bytes))
}
#[must_use]
#[inline(always)]
pub const fn from_ne_bytes(bytes: [u8; 2]) -> Self {
Self::from_bits(u16::from_ne_bytes(bytes))
}
#[must_use]
#[inline(always)]
pub fn total_cmp(&self, other: &Self) -> Ordering {
let mut left = self.to_bits() as i16;
let mut right = other.to_bits() as i16;
left ^= (((left >> 15) as u16) >> 1) as i16;
right ^= (((right >> 15) as u16) >> 1) as i16;
left.cmp(&right)
}
#[must_use]
#[inline(always)]
pub fn clamp(self, min: bf16, max: bf16) -> bf16 {
assert!(min <= max);
let mut x = self;
if x < min {
x = min;
}
if x > max {
x = max;
}
x
}
}
macro_rules! from_int_impl {
($t:ty, $func:ident) => {
#[inline(always)]
pub const fn $func(value: $t) -> Self {
Self::from_f32(value as f32)
}
};
}
impl bf16 {
pub const ONE: bf16 = bf16(0x3F80u16);
pub const ZERO: bf16 = bf16(0x0000u16);
pub const NEG_ZERO: bf16 = bf16(0x8000u16);
pub const NEG_ONE: bf16 = bf16(0xBF80u16);
pub const E: bf16 = bf16(0x402Eu16);
pub const PI: bf16 = bf16(0x4049u16);
pub const FRAC_1_PI: bf16 = bf16(0x3EA3u16);
pub const FRAC_1_SQRT_2: bf16 = bf16(0x3F35u16);
pub const FRAC_2_PI: bf16 = bf16(0x3F23u16);
pub const FRAC_2_SQRT_PI: bf16 = bf16(0x3F90u16);
pub const FRAC_PI_2: bf16 = bf16(0x3FC9u16);
pub const FRAC_PI_3: bf16 = bf16(0x3F86u16);
pub const FRAC_PI_4: bf16 = bf16(0x3F49u16);
pub const FRAC_PI_6: bf16 = bf16(0x3F06u16);
pub const FRAC_PI_8: bf16 = bf16(0x3EC9u16);
pub const LN_10: bf16 = bf16(0x4013u16);
pub const LN_2: bf16 = bf16(0x3F31u16);
pub const LOG10_E: bf16 = bf16(0x3EDEu16);
pub const LOG10_2: bf16 = bf16(0x3E9Au16);
pub const LOG2_E: bf16 = bf16(0x3FB9u16);
pub const LOG2_10: bf16 = bf16(0x4055u16);
pub const SQRT_2: bf16 = bf16(0x3FB5u16);
#[inline(always)]
pub const fn as_f32(self) -> f32 {
f32_from_bits((self.0 as u32) << 16)
}
#[inline(always)]
pub const fn from_f32(value: f32) -> Self {
let bits = f32_to_bits(value);
let truncated = bits as u16;
let bf16_bits = (bits >> 16) as u16;
let halfway = 1u16 << 15;
let is_odd = bf16_bits % 2 == 1;
let is_halfway = truncated == halfway;
let is_above = truncated > halfway;
let round_up = is_above || (is_halfway && is_odd);
Self::from_bits(bf16_bits + round_up as u16)
}
from_int_impl!(u8, from_u8);
from_int_impl!(u16, from_u16);
from_int_impl!(u32, from_u32);
from_int_impl!(u64, from_u64);
from_int_impl!(u128, from_u128);
from_int_impl!(i8, from_i8);
from_int_impl!(i16, from_i16);
from_int_impl!(i32, from_i32);
from_int_impl!(i64, from_i64);
from_int_impl!(i128, from_i128);
from_int_impl!(f64, from_f64);
}
impl PartialEq for bf16 {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
eq(*self, *other)
}
}
impl PartialOrd for bf16 {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
partial_cmp(*self, *other)
}
#[inline(always)]
fn lt(&self, other: &bf16) -> bool {
lt(*self, *other)
}
#[inline(always)]
fn le(&self, other: &bf16) -> bool {
le(*self, *other)
}
#[inline(always)]
fn gt(&self, other: &bf16) -> bool {
gt(*self, *other)
}
#[inline(always)]
fn ge(&self, other: &bf16) -> bool {
ge(*self, *other)
}
}
impl Add for bf16 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() + rhs.as_f32())
}
}
op_impl!(bf16, Add, AddAssign, add, add_assign);
impl Div for bf16 {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() / rhs.as_f32())
}
}
op_impl!(bf16, Div, DivAssign, div, div_assign);
impl Mul for bf16 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() * rhs.as_f32())
}
}
op_impl!(bf16, Mul, MulAssign, mul, mul_assign);
impl Sub for bf16 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() - rhs.as_f32())
}
}
op_impl!(bf16, Sub, SubAssign, sub, sub_assign);
impl Rem for bf16 {
type Output = Self;
#[inline(always)]
fn rem(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() % rhs.as_f32())
}
}
op_impl!(bf16, Rem, RemAssign, rem, rem_assign);
impl Neg for bf16 {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
Self::from_bits(self.0 ^ (1 << 15))
}
}
ref_impl!(bf16, Neg, neg);
impl str::FromStr for bf16 {
type Err = num::ParseFloatError;
fn from_str(src: &str) -> Result<bf16, num::ParseFloatError> {
f32::from_str(src).map(bf16::from_f32)
}
}
impl fmt::Debug for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.as_f32(), f)
}
}
impl fmt::Display for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(&self.as_f32(), f)
}
}
impl fmt::LowerExp for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:e}", self.as_f32())
}
}
impl fmt::UpperExp for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:E}", self.as_f32())
}
}
impl fmt::Binary for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:b}", self.0)
}
}
impl fmt::Octal for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:o}", self.0)
}
}
impl fmt::LowerHex for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:x}", self.0)
}
}
impl fmt::UpperHex for bf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:X}", self.0)
}
}
impl Product for bf16 {
#[inline(always)]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
product_bf16(iter.map(|f| f.to_bits()))
}
}
impl<'a> Product<&'a bf16> for bf16 {
#[inline]
fn product<I: Iterator<Item = &'a bf16>>(iter: I) -> Self {
product_bf16(iter.map(|f| f.to_bits()))
}
}
impl Sum for bf16 {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
sum_bf16(iter.map(|f| f.to_bits()))
}
}
impl<'a> Sum<&'a bf16> for bf16 {
#[inline]
fn sum<I: Iterator<Item = &'a bf16>>(iter: I) -> Self {
sum_bf16(iter.map(|f| f.to_bits()))
}
}
from_impl!(bf16, u8, from_u8);
from_impl!(bf16, u16, from_u16);
from_impl!(bf16, u32, from_u32);
from_impl!(bf16, u64, from_u64);
from_impl!(bf16, u128, from_u128);
from_impl!(bf16, i8, from_i8);
from_impl!(bf16, i16, from_i16);
from_impl!(bf16, i32, from_i32);
from_impl!(bf16, i64, from_i64);
from_impl!(bf16, i128, from_i128);
from_impl!(bf16, f64, from_f64);
#[must_use]
#[inline(always)]
const fn eq(lhs: bf16, rhs: bf16) -> bool {
if lhs.is_nan() {
false
} else if lhs.to_bits() & !bf16::SIGN_MASK == 0 {
rhs.to_bits() & !bf16::SIGN_MASK == 0
} else {
lhs.to_bits() == rhs.to_bits()
}
}
#[must_use]
#[inline(always)]
fn partial_cmp(lhs: bf16, rhs: bf16) -> Option<Ordering> {
if lhs.is_nan() || rhs.is_nan() {
None
} else {
let neg = lhs.0 & 0x8000u16 != 0;
let rhs_neg = rhs.0 & 0x8000u16 != 0;
match (neg, rhs_neg) {
(false, false) => Some(lhs.0.cmp(&rhs.0)),
(false, true) => {
if (lhs.0 | rhs.0) & 0x7FFFu16 == 0 {
Some(Ordering::Equal)
} else {
Some(Ordering::Greater)
}
},
(true, false) => {
if (lhs.0 | rhs.0) & 0x7FFFu16 == 0 {
Some(Ordering::Equal)
} else {
Some(Ordering::Less)
}
},
(true, true) => Some(rhs.0.cmp(&lhs.0)),
}
}
}
#[must_use]
#[inline(always)]
const fn lt(lhs: bf16, rhs: bf16) -> bool {
if lhs.is_nan() || rhs.is_nan() {
false
} else {
let neg = lhs.0 & 0x8000u16 != 0;
let rhs_neg = rhs.0 & 0x8000u16 != 0;
match (neg, rhs_neg) {
(false, false) => lhs.0 < rhs.0,
(false, true) => false,
(true, false) => (lhs.0 | rhs.0) & 0x7FFFu16 != 0,
(true, true) => lhs.0 > rhs.0,
}
}
}
#[must_use]
#[inline(always)]
const fn le(lhs: bf16, rhs: bf16) -> bool {
if lhs.is_nan() || rhs.is_nan() {
false
} else {
let neg = lhs.0 & 0x8000u16 != 0;
let rhs_neg = rhs.0 & 0x8000u16 != 0;
match (neg, rhs_neg) {
(false, false) => lhs.0 <= rhs.0,
(false, true) => (lhs.0 | rhs.0) & 0x7FFFu16 == 0,
(true, false) => true,
(true, true) => lhs.0 >= rhs.0,
}
}
}
#[must_use]
#[inline(always)]
const fn gt(lhs: bf16, rhs: bf16) -> bool {
if lhs.is_nan() || rhs.is_nan() {
false
} else {
let neg = lhs.0 & 0x8000u16 != 0;
let rhs_neg = rhs.0 & 0x8000u16 != 0;
match (neg, rhs_neg) {
(false, false) => lhs.0 > rhs.0,
(false, true) => (lhs.0 | rhs.0) & 0x7FFFu16 != 0,
(true, false) => false,
(true, true) => lhs.0 < rhs.0,
}
}
}
#[must_use]
#[inline(always)]
const fn ge(lhs: bf16, rhs: bf16) -> bool {
if lhs.is_nan() || rhs.is_nan() {
false
} else {
let neg = lhs.0 & 0x8000u16 != 0;
let rhs_neg = rhs.0 & 0x8000u16 != 0;
match (neg, rhs_neg) {
(false, false) => lhs.0 >= rhs.0,
(false, true) => true,
(true, false) => (lhs.0 | rhs.0) & 0x7FFFu16 == 0,
(true, true) => lhs.0 <= rhs.0,
}
}
}
#[must_use]
#[inline(always)]
const fn f32_from_bits(v: u32) -> f32 {
unsafe { mem::transmute(v) }
}
#[must_use]
#[inline(always)]
const fn f32_to_bits(v: f32) -> u32 {
unsafe { mem::transmute(v) }
}
#[must_use]
#[inline(always)]
const fn u16_to_f32(v: u16) -> f32 {
bf16(v).as_f32()
}
#[must_use]
#[inline(always)]
fn product_bf16<I: Iterator<Item = u16>>(iter: I) -> bf16 {
bf16::from_f32(iter.map(u16_to_f32).product())
}
#[must_use]
#[inline(always)]
fn sum_bf16<I: Iterator<Item = u16>>(iter: I) -> bf16 {
bf16::from_f32(iter.map(u16_to_f32).sum())
}