macro_rules! make_helper {
($Float:ident($Bits:ty, $IBits:ident, $prec:expr) $(; use $path:path)?) => {
#[allow(non_snake_case)]
pub mod $Float {
use crate::{
helpers::{FloatKind, ToFixedHelper, ToFloatHelper, Widest},
int_helper,
};
use core::cmp::Ordering;
$(use $path;)?
const PREC: u32 = $prec;
const EXP_BIAS: i32 = (1 << (<$Bits>::BITS - PREC - 1)) - 1;
const EXP_MIN: i32 = 1 - EXP_BIAS;
const EXP_MAX: i32 = EXP_BIAS;
pub const SIGN_MASK: $Bits = 1 << (<$Bits>::BITS - 1);
pub const EXP_MASK: $Bits = !(SIGN_MASK | MANT_MASK);
const MANT_MASK: $Bits = (1 << (PREC - 1)) - 1;
#[inline]
fn parts(val: $Float) -> (bool, i32, $Bits) {
let bits = val.to_bits();
let neg = bits & SIGN_MASK != 0;
let biased_exp = (bits & EXP_MASK) >> (PREC - 1);
let exp = biased_exp as i32 - EXP_BIAS;
let mant = bits & MANT_MASK;
(neg, exp, mant)
}
#[inline]
pub fn from_to_float_helper(
val: ToFloatHelper,
frac_bits: u32,
int_bits: u32,
) -> $Float {
let fix_bits = frac_bits + int_bits;
let bits_sign = if val.neg { SIGN_MASK } else { 0 };
let extra_zeros = 128 - fix_bits;
let leading_zeros = val.abs.leading_zeros() - extra_zeros;
let signif_bits = fix_bits - leading_zeros;
if signif_bits == 0 {
return $Float::from_bits(bits_sign);
}
let mut mantissa = val.abs << leading_zeros << 1;
let exponent = int_bits as i32 - 1 - leading_zeros as i32;
let biased_exponent = if exponent > EXP_MAX {
return $Float::from_bits(EXP_MASK | bits_sign);
} else if exponent < EXP_MIN {
let lost_prec = EXP_MIN - exponent;
if lost_prec as u32 >= (int_bits + frac_bits) {
mantissa = 0;
} else {
mantissa = (mantissa >> 1) | !(!0 >> 1);
mantissa >>= lost_prec - 1;
}
0
} else {
(exponent + EXP_MAX) as $Bits
};
let round_up = (fix_bits >= PREC) && {
let shift = PREC - 1;
let mid_bit = !(!0 >> 1) >> (shift + extra_zeros);
let lower_bits = mid_bit - 1;
if mantissa & mid_bit == 0 {
false
} else if mantissa & lower_bits != 0 {
true
} else {
mantissa & (mid_bit << 1) != 0
}
};
let bits_exp = biased_exponent << (PREC - 1);
let bits_mantissa = (if fix_bits >= PREC - 1 {
(mantissa >> (fix_bits - (PREC - 1))) as $Bits
} else {
(mantissa as $Bits) << (PREC - 1 - fix_bits)
}) & !(!0 << (PREC - 1));
let mut bits_exp_mantissa = bits_exp | bits_mantissa;
if round_up {
bits_exp_mantissa += 1;
}
$Float::from_bits(bits_sign | bits_exp_mantissa)
}
#[inline]
pub fn to_float_kind(val: $Float, dst_frac_bits: u32, dst_int_bits: u32) -> FloatKind {
let prec = PREC as i32;
let (neg, exp, mut mantissa) = parts(val);
if exp > EXP_MAX {
if mantissa == 0 {
return FloatKind::Infinite { neg };
} else {
return FloatKind::NaN;
};
}
if exp >= EXP_MIN {
mantissa |= 1 << (prec - 1);
}
if mantissa == 0 {
let conv = ToFixedHelper {
bits: Widest::Unsigned(0),
dir: Ordering::Equal,
overflow: false,
};
return FloatKind::Finite { neg, conv };
}
let mut src_frac_bits = prec - 1 - exp;
let need_to_shr = src_frac_bits - dst_frac_bits as i32;
if need_to_shr > prec {
let dir = if neg {
Ordering::Greater
} else {
Ordering::Less
};
let conv = ToFixedHelper {
bits: Widest::Unsigned(0),
dir,
overflow: false,
};
return FloatKind::Finite { neg, conv };
}
let mut dir = Ordering::Equal;
if need_to_shr > 0 {
let removed_bits = mantissa & !(!0 << need_to_shr);
let will_be_lsb = 1 << need_to_shr;
let tie = will_be_lsb >> 1;
if removed_bits == 0 {
} else if removed_bits < tie {
dir = Ordering::Less;
} else if removed_bits > tie || mantissa & will_be_lsb != 0 {
mantissa += will_be_lsb;
dir = Ordering::Greater;
} else {
dir = Ordering::Less;
};
mantissa >>= need_to_shr;
src_frac_bits -= need_to_shr;
}
let mut mantissa = mantissa as $IBits;
if neg {
mantissa = -mantissa;
dir = dir.reverse();
}
let mut conv = int_helper::$IBits::to_fixed_helper(
mantissa,
src_frac_bits,
dst_frac_bits,
dst_int_bits,
);
conv.dir = dir;
FloatKind::Finite { neg, conv }
}
}
};
}
make_helper! { f16(u16, i16, 11); use half::f16 }
make_helper! { bf16(u16, i16, 8); use half::bf16 }
make_helper! { f32(u32, i32, 24) }
make_helper! { f64(u64, i64, 53) }
make_helper! { F128Bits(u128, i128, 113); use crate::F128Bits }