trait SpecialValues {
const HAS_INFINITY: bool;
}
struct Ieee;
impl SpecialValues for Ieee {
const HAS_INFINITY: bool = true;
}
struct Finite;
impl SpecialValues for Finite {
const HAS_INFINITY: bool = false;
}
const F32_MANT_BITS: u32 = 23;
const F32_EXP_BIAS: i32 = 127;
const F32_EXP_ALL_ONES: u32 = 0xFF;
#[inline]
const fn round_to_nearest_even(value: u32, drop: u32) -> u32 {
if drop == 0 {
return value;
}
if drop >= u32::BITS {
return 0;
}
let keep = value >> drop;
let dropped = value & ((1 << drop) - 1);
let half = 1u32 << (drop - 1);
if dropped > half || (dropped == half && (keep & 1) == 1) {
keep + 1
} else {
keep
}
}
#[inline]
#[must_use]
pub const fn widen<const E: u32, const M: u32>(bits: u32) -> u32 {
widen_with::<Ieee, E, M>(bits)
}
#[inline]
#[must_use]
pub(crate) const fn widen_finite<const E: u32, const M: u32>(bits: u32) -> u32 {
widen_with::<Finite, E, M>(bits)
}
#[inline]
#[must_use]
pub(crate) const fn widen_high_word<const E: u32, const M: u32>(bits: u32) -> u16 {
high_word::<M>(widen::<E, M>(bits))
}
#[inline]
#[must_use]
pub(crate) const fn widen_finite_high_word<const E: u32, const M: u32>(bits: u32) -> u16 {
high_word::<M>(widen_finite::<E, M>(bits))
}
const fn high_word<const M: u32>(bits: u32) -> u16 {
assert!(
M <= 7,
"high-word widening requires at most seven mantissa bits"
);
(bits >> 16) as u16
}
const fn widen_with<P: SpecialValues, const E: u32, const M: u32>(bits: u32) -> u32 {
let sign = (bits >> (E + M)) & 1;
let exp = (bits >> M) & ((1 << E) - 1);
let mant = bits & ((1 << M) - 1);
let bias: i32 = (1 << (E - 1)) - 1;
let f32_sign = sign << 31;
let mant_align = F32_MANT_BITS - M;
if exp == (1 << E) - 1 {
if P::HAS_INFINITY && mant == 0 {
f32_sign | (F32_EXP_ALL_ONES << F32_MANT_BITS)
} else {
let payload = mant << mant_align;
let payload = if payload == 0 {
1 << (F32_MANT_BITS - 1)
} else {
payload
};
f32_sign | (F32_EXP_ALL_ONES << F32_MANT_BITS) | payload
}
} else if exp == 0 {
if mant == 0 {
f32_sign } else {
let mut m = mant;
let mut shift: i32 = 0;
while (m & (1 << M)) == 0 {
m <<= 1;
shift += 1;
}
let f32_exp = F32_EXP_BIAS - bias - shift + 1;
if f32_exp > 0 {
let m = m & ((1 << M) - 1); f32_sign | ((f32_exp as u32) << F32_MANT_BITS) | (m << mant_align)
} else {
let scale = F32_MANT_BITS as i32 - 1 + f32_exp - M as i32;
let frac = if scale >= 0 {
m << scale
} else {
round_to_nearest_even(m, (-scale) as u32)
};
f32_sign | frac
}
}
} else {
let f32_exp = (exp as i32 - bias + F32_EXP_BIAS) as u32;
f32_sign | (f32_exp << F32_MANT_BITS) | (mant << mant_align)
}
}
#[inline]
#[must_use]
pub const fn narrow<const E: u32, const M: u32>(f32_bits: u32) -> u32 {
narrow_with::<Ieee, E, M>(f32_bits)
}
#[inline]
#[must_use]
pub(crate) const fn narrow_finite<const E: u32, const M: u32>(f32_bits: u32) -> u32 {
narrow_with::<Finite, E, M>(f32_bits)
}
const fn narrow_with<P: SpecialValues, const E: u32, const M: u32>(f32_bits: u32) -> u32 {
let sign = (f32_bits >> 31) & 1;
let f32_exp = ((f32_bits >> F32_MANT_BITS) & F32_EXP_ALL_ONES) as i32;
let f32_mant = f32_bits & ((1 << F32_MANT_BITS) - 1);
let bias: i32 = (1 << (E - 1)) - 1;
let exp_all_ones: u32 = (1 << E) - 1;
let out_sign = sign << (E + M);
let mant_align = F32_MANT_BITS - M;
let max_finite = out_sign | ((exp_all_ones - 1) << M) | ((1 << M) - 1);
if f32_exp == F32_EXP_ALL_ONES as i32 {
if f32_mant == 0 {
return if P::HAS_INFINITY {
out_sign | (exp_all_ones << M)
} else {
max_finite
};
}
if !P::HAS_INFINITY {
return out_sign | (exp_all_ones << M) | ((1 << M) - 1);
}
let payload = f32_mant >> mant_align;
let mant = if payload == 0 { 1 } else { payload };
return out_sign | (exp_all_ones << M) | mant;
}
let (unbiased, significand) = if f32_exp == 0 {
if f32_mant == 0 {
return out_sign; }
let shift = f32_mant.leading_zeros() - (u32::BITS - 1 - F32_MANT_BITS);
(1 - F32_EXP_BIAS - shift as i32, f32_mant << shift)
} else {
(f32_exp - F32_EXP_BIAS, f32_mant | (1 << F32_MANT_BITS))
};
let out_exp = unbiased + bias;
if out_exp >= exp_all_ones as i32 {
return if P::HAS_INFINITY {
out_sign | (exp_all_ones << M)
} else {
max_finite
};
}
if out_exp <= 0 {
let drop = mant_align as i32 + (1 - out_exp);
if drop >= u32::BITS as i32 {
return out_sign;
}
return out_sign | round_to_nearest_even(significand, drop as u32);
}
let reduced_sig = round_to_nearest_even(significand, mant_align);
let encoded = out_sign | (((out_exp as u32) << M) + (reduced_sig - (1 << M)));
if !P::HAS_INFINITY && ((encoded >> M) & exp_all_ones) == exp_all_ones {
max_finite
} else {
encoded
}
}