#![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 f16(u16);
unsafe impl Send for f16 {
}
unsafe impl Sync for f16 {
}
impl f16 {
pub const RADIX: u32 = 2;
pub const MANTISSA_DIGITS: u32 = <Self as Float>::MANTISSA_SIZE as u32 + 1;
pub const DIGITS: u32 = 3;
pub const EPSILON: Self = f16(0x1400u16);
pub const MIN: Self = <Self as Float>::MIN;
pub const MIN_POSITIVE: Self = Self(0x0400u16);
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 = -4;
pub const MAX_10_EXP: i32 = 4;
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
}
}
#[inline(always)]
pub const fn to_bits(self) -> u16 {
self.0
}
#[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: f16, max: f16) -> f16 {
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 {
f32_to_f16(value as f32)
}
};
}
impl f16 {
pub const ONE: f16 = f16(0x3C00u16);
pub const ZERO: f16 = f16(0x0000u16);
pub const NEG_ZERO: f16 = f16(0x8000u16);
pub const NEG_ONE: f16 = f16(0xBC00u16);
pub const E: f16 = f16(0x4170u16);
pub const PI: f16 = f16(0x4248u16);
pub const FRAC_1_PI: f16 = f16(0x3518u16);
pub const FRAC_1_SQRT_2: f16 = f16(0x39A8u16);
pub const FRAC_2_PI: f16 = f16(0x3918u16);
pub const FRAC_2_SQRT_PI: f16 = f16(0x3C83u16);
pub const FRAC_PI_2: f16 = f16(0x3E48u16);
pub const FRAC_PI_3: f16 = f16(0x3C30u16);
pub const FRAC_PI_4: f16 = f16(0x3A48u16);
pub const FRAC_PI_6: f16 = f16(0x3830u16);
pub const FRAC_PI_8: f16 = f16(0x3648u16);
pub const LN_10: f16 = f16(0x409Bu16);
pub const LN_2: f16 = f16(0x398Cu16);
pub const LOG10_E: f16 = f16(0x36F3u16);
pub const LOG10_2: f16 = f16(0x34D1u16);
pub const LOG2_E: f16 = f16(0x3DC5u16);
pub const LOG2_10: f16 = f16(0x42A5u16);
pub const SQRT_2: f16 = f16(0x3DA8u16);
#[inline(always)]
pub const fn as_f32(self) -> f32 {
f16_to_f32(self)
}
#[inline(always)]
pub const fn from_f32(value: f32) -> Self {
f32_to_f16(value)
}
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 f16 {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
eq(*self, *other)
}
}
impl PartialOrd for f16 {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
partial_cmp(*self, *other)
}
#[inline(always)]
fn lt(&self, other: &f16) -> bool {
lt(*self, *other)
}
#[inline(always)]
fn le(&self, other: &f16) -> bool {
le(*self, *other)
}
#[inline(always)]
fn gt(&self, other: &f16) -> bool {
gt(*self, *other)
}
#[inline(always)]
fn ge(&self, other: &f16) -> bool {
ge(*self, *other)
}
}
impl Add for f16 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() + rhs.as_f32())
}
}
op_impl!(f16, Add, AddAssign, add, add_assign);
impl Div for f16 {
type Output = Self;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() / rhs.as_f32())
}
}
op_impl!(f16, Div, DivAssign, div, div_assign);
impl Mul for f16 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() * rhs.as_f32())
}
}
op_impl!(f16, Mul, MulAssign, mul, mul_assign);
impl Sub for f16 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() - rhs.as_f32())
}
}
op_impl!(f16, Sub, SubAssign, sub, sub_assign);
impl Rem for f16 {
type Output = Self;
#[inline(always)]
fn rem(self, rhs: Self) -> Self::Output {
Self::from_f32(self.as_f32() % rhs.as_f32())
}
}
op_impl!(f16, Rem, RemAssign, rem, rem_assign);
impl Neg for f16 {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
Self::from_bits(self.0 ^ (1 << 15))
}
}
ref_impl!(f16, Neg, neg);
impl str::FromStr for f16 {
type Err = num::ParseFloatError;
fn from_str(src: &str) -> Result<f16, num::ParseFloatError> {
f32::from_str(src).map(f16::from_f32)
}
}
impl fmt::Debug for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.as_f32(), f)
}
}
impl fmt::Display for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(&self.as_f32(), f)
}
}
impl fmt::LowerExp for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:e}", self.as_f32())
}
}
impl fmt::UpperExp for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:E}", self.as_f32())
}
}
impl fmt::Binary for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:b}", self.0)
}
}
impl fmt::Octal for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:o}", self.0)
}
}
impl fmt::LowerHex for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:x}", self.0)
}
}
impl fmt::UpperHex for f16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{:X}", self.0)
}
}
impl Product for f16 {
#[inline(always)]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
product_f16(iter.map(|f| f.to_bits()))
}
}
impl<'a> Product<&'a f16> for f16 {
#[inline]
fn product<I: Iterator<Item = &'a f16>>(iter: I) -> Self {
product_f16(iter.map(|f| f.to_bits()))
}
}
impl Sum for f16 {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
sum_f16(iter.map(|f| f.to_bits()))
}
}
impl<'a> Sum<&'a f16> for f16 {
#[inline]
fn sum<I: Iterator<Item = &'a f16>>(iter: I) -> Self {
sum_f16(iter.map(|f| f.to_bits()))
}
}
from_impl!(f16, u8, from_u8);
from_impl!(f16, u16, from_u16);
from_impl!(f16, u32, from_u32);
from_impl!(f16, u64, from_u64);
from_impl!(f16, u128, from_u128);
from_impl!(f16, i8, from_i8);
from_impl!(f16, i16, from_i16);
from_impl!(f16, i32, from_i32);
from_impl!(f16, i64, from_i64);
from_impl!(f16, i128, from_i128);
from_impl!(f16, f64, from_f64);
#[must_use]
#[inline]
const fn f16_to_f32(half: f16) -> f32 {
let man_shift = f32::MANTISSA_SIZE - f16::MANTISSA_SIZE;
let f16_bias = f16::EXPONENT_BIAS - f16::MANTISSA_SIZE;
let f32_bias = f32::EXPONENT_BIAS - f32::MANTISSA_SIZE;
if half.0 & (f16::SIGN_MASK - 1) == 0 {
return f32_from_bits((half.0 as u32) << 16);
}
let half_sign = (half.0 & f16::SIGN_MASK) as u32;
let half_exp = (half.0 & f16::EXPONENT_MASK) as u32;
let half_man = (half.0 & f16::MANTISSA_MASK) as u32;
if half.is_nan() {
return f32_from_bits((half_sign << 16) | 0x7FC0_0000u32 | (half_man << man_shift));
} else if half.is_infinite() {
return f32_from_bits((half_sign << 16) | f32::INFINITY_BITS);
}
let sign = half_sign << 16;
let unbiased_exp = ((half_exp as i32) >> f16::MANTISSA_SIZE) - f16_bias;
if half_exp == 0 {
let e = (half_man as u16).leading_zeros() - (16 - f16::MANTISSA_SIZE as u32);
let exp = (f32_bias as u32 - f16_bias as u32 - e) << f32::MANTISSA_SIZE;
let man = (half_man << (f16_bias as u32 - 1 + e)) & f32::MANTISSA_MASK;
return f32_from_bits(sign | exp | man);
}
let exp = ((unbiased_exp + f32_bias) as u32) << f32::MANTISSA_SIZE;
let man = (half_man & f16::MANTISSA_MASK as u32) << man_shift;
f32_from_bits(sign | exp | man)
}
#[must_use]
#[inline]
const fn f32_to_f16(value: f32) -> f16 {
let man_shift = f32::MANTISSA_SIZE - f16::MANTISSA_SIZE;
let f16_bias = f16::EXPONENT_BIAS - f16::MANTISSA_SIZE;
let f32_bias = f32::EXPONENT_BIAS - f32::MANTISSA_SIZE;
let x = f32_to_bits(value);
let sign = x & f32::SIGN_MASK;
let exp = x & f32::EXPONENT_MASK;
let man = x & f32::MANTISSA_MASK;
if f32_is_nan(value) {
return f16::from_bits((sign >> 16) as u16 | 0x7e00 | (man >> man_shift) as u16);
} else if f32_is_infinite(value) {
return f16::from_bits((sign >> 16) as u16 | f16::INFINITY_BITS);
}
let half_sign = sign >> 16;
let unbiased_exp = ((exp >> f32::MANTISSA_SIZE) as i32) - f32_bias;
let half_exp = unbiased_exp + f16_bias;
if unbiased_exp >= 0x1F {
return f16::from_bits(half_sign as u16 | f16::INFINITY_BITS);
}
if half_exp <= 0 {
if f16_bias - 1 - half_exp > f32::MANTISSA_SIZE + 1 {
return f16::from_bits(half_sign as u16);
}
let man = man | f32::HIDDEN_BIT_MASK;
let mut half_man = man >> (f16_bias - 1 - half_exp);
let round_bit = 1 << (man_shift - half_exp);
if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
half_man += 1;
}
return f16::from_bits((half_sign | half_man) as u16);
}
let half_exp = (half_exp as u32) << f16::MANTISSA_SIZE;
let half_man = man >> man_shift;
let round_bit = 1 << (man_shift - 1);
if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
f16::from_bits(((half_sign | half_exp | half_man) + 1) as u16)
} else {
f16::from_bits((half_sign | half_exp | half_man) as u16)
}
}
#[must_use]
#[inline(always)]
const fn eq(lhs: f16, rhs: f16) -> bool {
if lhs.is_nan() {
false
} else if lhs.to_bits() & !f16::SIGN_MASK == 0 {
rhs.to_bits() & !f16::SIGN_MASK == 0
} else {
lhs.to_bits() == rhs.to_bits()
}
}
#[must_use]
#[inline(always)]
fn partial_cmp(lhs: f16, rhs: f16) -> 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: f16, rhs: f16) -> 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: f16, rhs: f16) -> 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: f16, rhs: f16) -> 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: f16, rhs: f16) -> 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_is_special(v: f32) -> bool {
f32_to_bits(v) & f32::EXPONENT_MASK == f32::EXPONENT_MASK
}
#[must_use]
#[inline(always)]
const fn f32_is_nan(v: f32) -> bool {
f32_is_special(v) && (f32_to_bits(v) & f32::MANTISSA_MASK) != 0
}
#[must_use]
#[inline(always)]
const fn f32_is_infinite(v: f32) -> bool {
f32_is_special(v) && (f32_to_bits(v) & f32::MANTISSA_MASK) == 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 {
f16_to_f32(f16(v))
}
#[must_use]
#[inline(always)]
fn product_f16<I: Iterator<Item = u16>>(iter: I) -> f16 {
f32_to_f16(iter.map(u16_to_f32).product())
}
#[must_use]
#[inline(always)]
fn sum_f16<I: Iterator<Item = u16>>(iter: I) -> f16 {
f32_to_f16(iter.map(u16_to_f32).sum())
}