use fixed::traits::{FromFixed, ToFixed};
pub use fixed::types::U68F60 as Fixed128;
use std::fmt::Display;
pub const FRACTION_ONE_SCALED: u128 = Fixed128::ONE.to_bits();
#[inline]
pub const fn bps_u128_to_fraction(bps: u128) -> Fixed128 {
if bps == 1_000_000 {
return Fixed128::ONE;
}
Fixed128::const_from_int(bps).unwrapped_div_int(1_000_000)
}
pub trait FixedExtra {
fn to_bps<Dst: FromFixed>(&self) -> Option<Dst>;
fn from_bps<Src: ToFixed>(bps: Src) -> Self;
fn to_display(&self) -> FixedSingleDisplay<'_>;
}
impl FixedExtra for Fixed128 {
#[inline]
fn to_bps<Dst: FromFixed>(&self) -> Option<Dst> {
(self * 1_000_000).round().checked_to_num()
}
#[inline]
fn from_bps<Src: ToFixed>(bps: Src) -> Self {
let bps = Fixed128::from_num(bps);
bps / 1_000_000
}
#[inline]
fn to_display(&self) -> FixedSingleDisplay<'_> {
FixedSingleDisplay(self)
}
}
pub struct FixedSingleDisplay<'a>(&'a Fixed128);
impl Display for FixedSingleDisplay<'_> {
fn fmt(&self, formater: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let sf = self.0.to_bits();
const ROUND_COMP: u128 = (1 << Fixed128::FRAC_NBITS) / (10_000 * 2);
let sf = sf + ROUND_COMP;
let i = sf >> Fixed128::FRAC_NBITS;
const FRAC_MASK: u128 = (1 << Fixed128::FRAC_NBITS) - 1;
let f_p = (sf & FRAC_MASK) as u64;
let f_p = ((f_p >> 30) * 10_000) >> 30;
write!(formater, "{i}.{f_p:0>4}")
}
}