lilliput-float 0.1.0

IEEE-754-compliant float-packing implementation, used in lilliput-core
Documentation
use crate::floats::{F16, F24, F32, F40, F48, F56, F64, F8};

pub trait FpRepr: Sized + Copy + PartialEq + PartialOrd {
    type Bits;

    const ZERO: Self;
    const ONE: Self;

    const MIN: Self;
    const MAX: Self;
    const MIN_POSITIVE: Self;

    const INFINITY: Self;
    const NEG_INFINITY: Self;

    const BITS: u32;
    const SIGN_BITS: u32;
    const EXPONENT_BITS: u32;
    const SIGNIFICAND_BITS: u32;

    const EXPONENT_MAX: Self::Bits;
    const EXPONENT_BIAS: Self::Bits;

    const SIGN_MASK: Self::Bits;
    const EXPONENT_MASK: Self::Bits;
    const SIGNIFICAND_MASK: Self::Bits;

    const IMPLICIT_BIT: Self::Bits;
}

macro_rules! impl_float_repr {
    ($t:ty, bytes: [u8; $bytes:expr], bits: $bits:ty, sign: 1, exponent: $exponent:expr, significand: $significand:expr) => {
        impl FpRepr for $t {
            type Bits = $bits;

            const ZERO: Self = Self(0);
            const ONE: Self = Self((Self::EXPONENT_MASK >> 1) & Self::EXPONENT_MASK);

            const MIN: Self = Self(
                Self::SIGN_MASK
                    | ((Self::EXPONENT_MASK << 1) & Self::EXPONENT_MASK)
                    | Self::SIGNIFICAND_MASK,
            );
            const MAX: Self =
                Self(((Self::EXPONENT_MASK << 1) & Self::EXPONENT_MASK) | Self::SIGNIFICAND_MASK);
            const MIN_POSITIVE: Self = Self(1 << Self::SIGNIFICAND_BITS);

            const INFINITY: Self = Self(Self::EXPONENT_MASK);
            const NEG_INFINITY: Self = Self(Self::SIGN_MASK | Self::EXPONENT_MASK);

            const BITS: u32 = Self::SIGN_BITS + Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS;
            const SIGN_BITS: u32 = 1;
            const EXPONENT_BITS: u32 = $exponent;
            const SIGNIFICAND_BITS: u32 = $significand;

            const EXPONENT_MAX: Self::Bits = (1 << Self::EXPONENT_BITS) - 1;
            const EXPONENT_BIAS: Self::Bits = (Self::EXPONENT_MAX >> 1) as Self::Bits;

            const SIGN_MASK: Self::Bits = 1 << (Self::BITS - 1);
            const EXPONENT_MASK: Self::Bits = (Self::SIGN_MASK - 1) & !Self::SIGNIFICAND_MASK;
            const SIGNIFICAND_MASK: Self::Bits = (1 << Self::SIGNIFICAND_BITS) - 1;
            const IMPLICIT_BIT: Self::Bits = 1 << Self::SIGNIFICAND_BITS;
        }
    };
}

impl_float_repr!(F8, bytes: [u8; 1], bits: u8, sign: 1, exponent: 4, significand: 3);
impl_float_repr!(F16, bytes: [u8; 2], bits: u16, sign: 1, exponent: 5, significand: 10);
impl_float_repr!(F24, bytes: [u8; 3], bits: u32, sign: 1, exponent: 7, significand: 16);
impl_float_repr!(F32, bytes: [u8; 4], bits: u32, sign: 1, exponent: 8, significand: 23);
impl_float_repr!(F40, bytes: [u8; 5], bits: u64, sign: 1, exponent: 8, significand: 31);
impl_float_repr!(F48, bytes: [u8; 6], bits: u64, sign: 1, exponent: 9, significand: 38);
impl_float_repr!(F56, bytes: [u8; 7], bits: u64, sign: 1, exponent: 10, significand: 45);
impl_float_repr!(F64, bytes: [u8; 8], bits: u64, sign: 1, exponent: 11, significand: 52);

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

    #[test]
    fn f32_matches_native_behavior() {
        assert_eq!(F32::ZERO, F32::from(0.0_f32));
        assert_eq!(F32::ONE, F32::from(1.0_f32));
        assert_eq!(F32::MIN, F32::from(f32::MIN));
        assert_eq!(F32::MAX, F32::from(f32::MAX));
        assert_eq!(F32::MIN_POSITIVE, F32::from(f32::MIN_POSITIVE));
        assert_eq!(F32::INFINITY, F32::from(f32::INFINITY));
        assert_eq!(F32::NEG_INFINITY, F32::from(f32::NEG_INFINITY));
    }

    #[test]
    fn f64_matches_native_behavior() {
        assert_eq!(F64::ZERO, F64::from(0.0_f64));
        assert_eq!(F64::ONE, F64::from(1.0_f64));
        assert_eq!(F64::MIN, F64::from(f64::MIN));
        assert_eq!(F64::MAX, F64::from(f64::MAX));
        assert_eq!(F64::MIN_POSITIVE, F64::from(f64::MIN_POSITIVE));
        assert_eq!(F64::INFINITY, F64::from(f64::INFINITY));
        assert_eq!(F64::NEG_INFINITY, F64::from(f64::NEG_INFINITY));
    }
}