lilliput-float 0.1.0

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

pub trait FpPack {
    type Validator;

    fn pack_native(self, validator: &Self::Validator) -> PackedFloat;
    fn pack_optimal(self, validator: &Self::Validator) -> PackedFloat;
}

macro_rules! truncate_validated {
    ($src:ty => $dst:ty, $native:expr, $validate:expr) => {{
        let (native, validate) = ($native, $validate);

        let non_packed_src: $src = native.into();
        let (packed_src, packed_dst): ($src, $dst) = non_packed_src.truncate();

        if (validate)(non_packed_src, packed_src) {
            if packed_dst.classify() == packed_src.classify() {
                Some(packed_dst)
            } else {
                None
            }
        } else {
            None
        }
    }};
}

impl FpPack for F32 {
    type Validator = PackedFloatValidator<f32>;

    #[inline]
    fn pack_native(self, validator: &Self::Validator) -> PackedFloat {
        #[allow(unused_variables)]
        let non_packed: f32 = self.into();

        #[allow(unused_variables)]
        let validate = |value: F32, packed: F32| {
            let value: f32 = value.into();
            let packed: f32 = packed.into();
            validator.validate(value, packed)
        };

        PackedFloat::F32(self)
    }

    #[inline]
    fn pack_optimal(self, validator: &Self::Validator) -> PackedFloat {
        let non_packed: f32 = self.into();

        let validate = |value: F32, packed: F32| {
            let value: f32 = value.into();
            let packed: f32 = packed.into();
            validator.validate(value, packed)
        };

        if let Some(packed) = truncate_validated!(F32 => F16, non_packed, validate) {
            if let Some(packed) = truncate_validated!(F32 => F8, non_packed, validate) {
                PackedFloat::F8(packed)
            } else {
                PackedFloat::F16(packed)
            }
        } else {
            #[allow(clippy::collapsible_else_if)]
            if let Some(packed) = truncate_validated!(F32 => F24, non_packed, validate) {
                PackedFloat::F24(packed)
            } else {
                PackedFloat::F32(self)
            }
        }
    }
}

impl FpPack for F64 {
    type Validator = PackedFloatValidator<f64>;

    #[inline]
    fn pack_native(self, validator: &Self::Validator) -> PackedFloat {
        let non_packed: f64 = self.into();

        let validate = |value: F64, packed: F64| {
            let value: f64 = value.into();
            let packed: f64 = packed.into();
            validator.validate(value, packed)
        };

        if let Some(packed) = truncate_validated!(F64 => F32, non_packed, validate) {
            PackedFloat::F32(packed)
        } else {
            PackedFloat::F64(self)
        }
    }

    #[inline]
    fn pack_optimal(self, validator: &Self::Validator) -> PackedFloat {
        let non_packed: f64 = self.into();

        let validate = |value: F64, packed: F64| {
            let value: f64 = value.into();
            let packed: f64 = packed.into();
            validator.validate(value, packed)
        };

        if let Some(packed) = truncate_validated!(F64 => F32, non_packed, validate) {
            if let Some(packed) = truncate_validated!(F64 => F16, non_packed, validate) {
                if let Some(packed) = truncate_validated!(F64 => F8, non_packed, validate) {
                    PackedFloat::F8(packed)
                } else {
                    PackedFloat::F16(packed)
                }
            } else if let Some(packed) = truncate_validated!(F64 => F24, non_packed, validate) {
                PackedFloat::F24(packed)
            } else {
                PackedFloat::F32(packed)
            }
        } else if let Some(packed) = truncate_validated!(F64 => F48, non_packed, validate) {
            if let Some(packed) = truncate_validated!(F64 => F40, non_packed, validate) {
                PackedFloat::F40(packed)
            } else {
                PackedFloat::F48(packed)
            }
        } else if let Some(packed) = truncate_validated!(F64 => F56, non_packed, validate) {
            PackedFloat::F56(packed)
        } else {
            PackedFloat::F64(self)
        }
    }
}