i_float 4.0.0

This fixed float math library provides an efficient and deterministic solution for arithmetic and geometric operations.
Documentation
use crate::float::number::FloatNumber;
use crate::int::number::fixed_scale::FixedScale;
use crate::int::number::int::IntNumber;
use crate::int::number::uint::UIntNumber;
use core::fmt::Display;
use core::ops::{Add, BitAnd, Div, Mul, Neg, Shl, Shr, Sub};

pub trait WideIntNumber:
    Copy
    + Ord
    + Send
    + Sync
    + Display
    + Add<Output = Self>
    + BitAnd<Output = Self>
    + Sub<Output = Self>
    + Mul<Output = Self>
    + Div<Output = Self>
    + Neg<Output = Self>
    + Shl<u32, Output = Self>
    + Shr<u32, Output = Self>
{
    type UInt: UIntNumber;
    type Narrow: IntNumber;
    const MAX: Self;
    const MIN: Self;
    const ZERO: Self;
    const ONE: Self;
    const TWO: Self;
    const FOUR: Self;
    fn from_u32(value: u32) -> Self;
    fn from_usize(value: usize) -> Self;
    fn from_rounded_float<F: FloatNumber>(value: F) -> Self;
    fn from_uint(value: Self::UInt) -> Self;
    fn to_uint(self) -> Self::UInt;
    fn wrapping_add(self, rhs: Self) -> Self;
    fn wrapping_sub(self, rhs: Self) -> Self;
    fn wrapping_mul(self, rhs: Self) -> Self;
    fn unsigned_abs(self) -> Self::UInt;
    fn signum(self) -> Self;
    fn ilog2(self) -> u32;
    fn isqrt(self) -> Self;
    fn to_usize(self) -> usize;
    fn to_f32(self) -> f32;
    fn to_f64(self) -> f64;

    #[inline(always)]
    fn shr_round(self, shift: u32) -> Self {
        if shift == 0 {
            return self;
        }

        debug_assert!(shift < Self::UInt::BITS);

        let negative = self < Self::ZERO;
        let half = Self::UInt::ONE << (shift - 1);
        let rounded_abs = (self.unsigned_abs() + half) >> shift;

        if negative {
            if rounded_abs == Self::UInt::LAST_BIT {
                Self::MIN
            } else {
                -Self::from_uint(rounded_abs)
            }
        } else {
            Self::from_uint(rounded_abs)
        }
    }

    #[inline(always)]
    fn shr_round_positive(self, shift: u32) -> Self {
        debug_assert!(self >= Self::ZERO);

        if shift == 0 {
            return self;
        }

        debug_assert!(shift < Self::UInt::BITS);

        let half = Self::UInt::ONE << (shift - 1);
        Self::from_uint((self.to_uint() + half) >> shift)
    }

    #[inline(always)]
    fn to_scaled(self) -> Self {
        self << FixedScale::<Self::Narrow>::SHIFT
    }
}

impl WideIntNumber for i32 {
    type UInt = u32;
    type Narrow = i16;
    const MAX: Self = Self::MAX;
    const MIN: Self = Self::MIN;
    const ZERO: Self = 0;
    const ONE: Self = 1;
    const TWO: Self = 2;
    const FOUR: Self = 4;

    #[inline(always)]
    fn from_u32(value: u32) -> Self {
        value as Self
    }
    #[inline(always)]
    fn from_usize(value: usize) -> Self {
        value as Self
    }

    #[inline(always)]
    fn from_rounded_float<F: FloatNumber>(value: F) -> Self {
        value.to_round_i32()
    }
    #[inline(always)]
    fn from_uint(value: Self::UInt) -> Self {
        value as Self
    }

    #[inline(always)]
    fn to_uint(self) -> Self::UInt {
        self as Self::UInt
    }

    #[inline(always)]
    fn wrapping_add(self, rhs: Self) -> Self {
        self.wrapping_add(rhs)
    }

    #[inline(always)]
    fn wrapping_sub(self, rhs: Self) -> Self {
        self.wrapping_sub(rhs)
    }

    #[inline(always)]
    fn wrapping_mul(self, rhs: Self) -> Self {
        self.wrapping_mul(rhs)
    }

    #[inline(always)]
    fn unsigned_abs(self) -> Self::UInt {
        self.unsigned_abs()
    }

    #[inline(always)]
    fn signum(self) -> Self {
        self.signum()
    }
    #[inline(always)]
    fn ilog2(self) -> u32 {
        self.ilog2()
    }
    #[inline(always)]
    fn isqrt(self) -> Self {
        self.isqrt()
    }
    #[inline(always)]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline(always)]
    fn to_f32(self) -> f32 {
        self as f32
    }

    #[inline(always)]
    fn to_f64(self) -> f64 {
        self as f64
    }
}
impl WideIntNumber for i64 {
    type UInt = u64;
    type Narrow = i32;
    const MAX: Self = Self::MAX;
    const MIN: Self = Self::MIN;
    const ZERO: Self = 0;
    const ONE: Self = 1;
    const TWO: Self = 2;
    const FOUR: Self = 4;

    #[inline(always)]
    fn from_u32(value: u32) -> Self {
        value as Self
    }
    #[inline(always)]
    fn from_usize(value: usize) -> Self {
        value as Self
    }

    #[inline(always)]
    fn from_rounded_float<F: FloatNumber>(value: F) -> Self {
        value.to_round_i64()
    }

    #[inline(always)]
    fn from_uint(value: Self::UInt) -> Self {
        value as Self
    }

    #[inline(always)]
    fn to_uint(self) -> Self::UInt {
        self as Self::UInt
    }

    #[inline(always)]
    fn wrapping_add(self, rhs: Self) -> Self {
        self.wrapping_add(rhs)
    }

    #[inline(always)]
    fn wrapping_sub(self, rhs: Self) -> Self {
        self.wrapping_sub(rhs)
    }

    #[inline(always)]
    fn wrapping_mul(self, rhs: Self) -> Self {
        self.wrapping_mul(rhs)
    }

    #[inline(always)]
    fn unsigned_abs(self) -> Self::UInt {
        self.unsigned_abs()
    }
    #[inline(always)]
    fn signum(self) -> Self {
        self.signum()
    }
    #[inline(always)]
    fn ilog2(self) -> u32 {
        self.ilog2()
    }
    #[inline(always)]
    fn isqrt(self) -> Self {
        self.isqrt()
    }
    #[inline(always)]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline(always)]
    fn to_f32(self) -> f32 {
        self as f32
    }
    #[inline(always)]
    fn to_f64(self) -> f64 {
        self as f64
    }
}
impl WideIntNumber for i128 {
    type UInt = u128;
    type Narrow = i64;
    const MAX: Self = Self::MAX;
    const MIN: Self = Self::MIN;
    const ZERO: Self = 0;
    const ONE: Self = 1;
    const TWO: Self = 2;
    const FOUR: Self = 4;

    #[inline(always)]
    fn from_u32(value: u32) -> Self {
        value as Self
    }
    #[inline(always)]
    fn from_usize(value: usize) -> Self {
        value as Self
    }

    #[inline(always)]
    fn from_rounded_float<F: FloatNumber>(value: F) -> Self {
        value.to_round_i128()
    }

    #[inline(always)]
    fn from_uint(value: Self::UInt) -> Self {
        value as Self
    }

    #[inline(always)]
    fn to_uint(self) -> Self::UInt {
        self as Self::UInt
    }

    #[inline(always)]
    fn wrapping_add(self, rhs: Self) -> Self {
        self.wrapping_add(rhs)
    }

    #[inline(always)]
    fn wrapping_sub(self, rhs: Self) -> Self {
        self.wrapping_sub(rhs)
    }

    #[inline(always)]
    fn wrapping_mul(self, rhs: Self) -> Self {
        self.wrapping_mul(rhs)
    }

    #[inline(always)]
    fn unsigned_abs(self) -> Self::UInt {
        self.unsigned_abs()
    }
    #[inline(always)]
    fn signum(self) -> Self {
        self.signum()
    }
    #[inline(always)]
    fn ilog2(self) -> u32 {
        self.ilog2()
    }
    #[inline(always)]
    fn isqrt(self) -> Self {
        self.isqrt()
    }
    #[inline(always)]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline(always)]
    fn to_f32(self) -> f32 {
        self as f32
    }
    #[inline(always)]
    fn to_f64(self) -> f64 {
        self as f64
    }
}

#[cfg(test)]
mod tests {
    use super::WideIntNumber;

    #[test]
    fn shr_round_positive_rounds_to_nearest() {
        assert_eq!(100i64.shr_round_positive(4), 6);
        assert_eq!(104i64.shr_round_positive(4), 7);
        assert_eq!(111i64.shr_round_positive(4), 7);
        assert_eq!(112i64.shr_round_positive(4), 7);
    }

    #[test]
    fn shr_round_matches_half_away_from_zero() {
        assert_eq!(100i64.shr_round(4), 6);
        assert_eq!(104i64.shr_round(4), 7);
        assert_eq!((-100i64).shr_round(4), -6);
        assert_eq!((-104i64).shr_round(4), -7);
    }

    #[test]
    fn shr_round_with_zero_shift_returns_input() {
        assert_eq!(123i64.shr_round(0), 123);
        assert_eq!((-123i64).shr_round(0), -123);
        assert_eq!(123i64.shr_round_positive(0), 123);
    }

    #[test]
    fn shr_round_handles_min_value_abs() {
        assert_eq!(i64::MIN.shr_round(1), -(1i64 << 62));
    }
}