easy-cast 0.7.0

Type conversions which are expected to succeed
Documentation
use easy_cast::{Error, traits::*};

#[test]
fn int_to_float_exact_and_inexact() {
    assert_eq!(f32::try_conv(0u32), Ok(0.0));
    assert_eq!(f32::try_conv(1u32), Ok(1.0));
    assert_eq!(f32::try_conv(-0x00FF_FFFFi32), Ok(-16777215.0));
    assert_eq!(f32::try_conv(-0x01FF_FFFFi32), Err(Error::Inexact));
    assert_eq!(f32::try_conv(0xFFFF_FF00u32), Ok(4294967040.0));
    assert_eq!(f32::try_conv(0xFFFF_FF80u32), Err(Error::Inexact));

    assert_eq!(f64::try_conv(-0x0000_000F_FFFF_FFFFi64), Ok(-68719476735.0));
    assert_eq!(
        f64::try_conv(-0x001F_FFFF_FFFF_FFFFi64),
        Ok(-9007199254740991.0)
    );
    assert_eq!(
        f64::try_conv(-0x003F_FFFF_FFFF_FFFFi64),
        Err(Error::Inexact)
    );
    assert_eq!(
        f64::try_conv(0xFFFF_FFFF_FFFF_F800u64),
        Ok(18446744073709549568.0)
    );
    assert_eq!(f64::try_conv(0xFFFF_FFFF_FFFF_FC00u64), Err(Error::Inexact));

    assert_eq!(
        f32::try_conv(0xFFFFFF00_00000000_00000000_00000001_u128),
        Err(Error::Inexact)
    );
}

#[test]
fn int_to_float_approx() {
    // Duplicate the above tests but using try_conv_approx.
    // Note that values on the right may contain more precision than f32/f64
    // supports; the excess precision is discarded before testing for equality.

    assert_eq!(f32::try_conv_approx(0u32), Ok(0.0));
    assert_eq!(f32::try_conv_approx(1u32), Ok(1.0));
    assert_eq!(f32::try_conv_approx(-0x00FF_FFFFi32), Ok(-16777215.0));
    assert_eq!(f32::try_conv_approx(-0x01FF_FFFFi32), Ok(-33554431.0));
    assert_eq!(f32::try_conv_approx(0xFFFF_FF00u32), Ok(4294967040.0));
    assert_eq!(f32::try_conv_approx(0xFFFF_FF80u32), Ok(4294967168.0));

    assert_eq!(
        f64::try_conv_approx(-0x0000_000F_FFFF_FFFFi64),
        Ok(-68719476735.0)
    );
    assert_eq!(
        f64::try_conv_approx(-0x001F_FFFF_FFFF_FFFFi64),
        Ok(-9007199254740991.0)
    );
    assert_eq!(
        f64::try_conv_approx(-0x003F_FFFF_FFFF_FFFFi64),
        Ok(-18014398509481983.0)
    );
    assert_eq!(
        f64::try_conv_approx(0xFFFF_FFFF_FFFF_F800u64),
        Ok(18446744073709549568.0)
    );
    assert_eq!(
        f64::try_conv_approx(0xFFFF_FFFF_FFFF_FC00u64),
        Ok(18446744073709550592.0)
    );

    assert_eq!(
        f32::try_conv_approx(0xFFFFFF00_00000000_00000000_00000001_u128),
        Ok(f32::MAX)
    );
}

#[cfg(any(feature = "std", feature = "libm"))]
#[test]
fn int_to_float_nearest() {
    use easy_cast::Nearest;

    // Repeat a few of the above tests using Nearest rounding (equivalent)
    assert_eq!(f32::try_conv_to(Nearest, 0xFFFF_FF00u32), Ok(4294967040.0));
    assert_eq!(f32::try_conv_to(Nearest, 0xFFFF_FF70u32), Ok(4294967040.0));
    assert_eq!(f32::try_conv_to(Nearest, 0xFFFF_FF80u32), Ok(4294967168.0));
    assert_eq!(
        f32::try_conv_to(Nearest, 0xFFFF_FFFF_FFFF_FC00u64),
        Ok(18446744073709550592.0)
    );
}

#[test]
fn int_to_float_overflow() {
    // f32::MAX as u128
    const MAX: u128 = 0xFFFFFF00_00000000_00000000_00000000_u128;
    // The maximum value approximating to f32::MAX
    const MAX_APPROX: u128 = 0xFFFFFF7F_FFFFFFFF_FFFFFFFF_FFFFFFFF_u128;

    assert_eq!(f32::try_conv(MAX), Ok(f32::MAX));
    assert_eq!(f32::try_conv(MAX + 1), Err(Error::Inexact));
    assert_eq!(f32::try_conv(MAX_APPROX + 1), Err(Error::Range));

    assert_eq!(f32::try_conv_approx(MAX), Ok(f32::MAX));
    assert_eq!(f32::try_conv_approx(MAX_APPROX), Ok(f32::MAX));
    assert_eq!(f32::try_conv_approx(MAX_APPROX + 1), Ok(f32::INFINITY));
    assert_eq!(f32::try_conv_approx(u128::MAX), Ok(f32::INFINITY));
}

#[test]
fn signed_integer_to_float_boundaries() {
    assert_eq!(f32::try_conv(i32::MIN), Ok(i32::MIN as f32));
    assert_eq!(f32::try_conv(i32::MIN + 1), Err(Error::Inexact));
    assert_eq!(f64::try_conv(i64::MIN), Ok(i64::MIN as f64));
    assert_eq!(f64::try_conv(i64::MIN + 1), Err(Error::Inexact));
}

#[test]
fn wide_integer_to_float_boundaries() {
    const F32_EXACT: u64 = 0x00FF_FFFF;
    const F32_INEXACT: u64 = 0x01FF_FFFF;
    const F64_EXACT: u64 = (1u64 << 53) - 1;
    const F64_INEXACT: u64 = (1u64 << 54) - 1;

    assert_eq!(f32::try_conv(F32_EXACT), Ok(F32_EXACT as f32));
    assert_eq!(f32::try_conv(F32_INEXACT), Err(Error::Inexact));
    assert_eq!(f64::try_conv(F64_EXACT), Ok(F64_EXACT as f64));
    assert_eq!(f64::try_conv(F64_INEXACT), Err(Error::Inexact));

    assert_eq!(f32::try_conv(F32_EXACT as i64), Ok(F32_EXACT as f32));
    assert_eq!(f32::try_conv(F32_INEXACT as i64), Err(Error::Inexact));
    assert_eq!(f64::try_conv(F64_EXACT as i128), Ok(F64_EXACT as f64));
    assert_eq!(f64::try_conv(F64_INEXACT as i128), Err(Error::Inexact));

    assert_eq!(f32::try_conv(F32_EXACT as u128), Ok(F32_EXACT as f32));
    assert_eq!(f32::try_conv(F32_INEXACT as u128), Err(Error::Inexact));
    assert_eq!(f64::try_conv(F64_EXACT as u128), Ok(F64_EXACT as f64));
    assert_eq!(f64::try_conv(F64_INEXACT as u128), Err(Error::Inexact));
}

#[test]
fn usize_and_isize_to_float() {
    assert_eq!(f32::try_conv(0usize), Ok(0.0));
    assert_eq!(f64::try_conv(1usize), Ok(1.0));
    assert_eq!(f32::try_conv(0isize), Ok(0.0));
    assert_eq!(f64::try_conv(-1isize), Ok(-1.0));

    if usize::BITS > f32::MANTISSA_DIGITS + 1 {
        let inexact = (1usize << ((f32::MANTISSA_DIGITS + 1) as usize)) - 1;
        assert_eq!(f32::try_conv(inexact), Err(Error::Inexact));
    }

    if isize::BITS > f64::MANTISSA_DIGITS + 1 {
        let inexact = (1isize << ((f64::MANTISSA_DIGITS + 1) as usize)) - 1;
        assert_eq!(f64::try_conv(inexact), Err(Error::Inexact));
        assert_eq!(f64::try_conv(-inexact), Err(Error::Inexact));
    }
}

#[test]
#[should_panic(expected = "cast x: u64 to f32: inexact for x = 33554431")]
fn integer_to_float_conv_panics_on_inexact_conversion() {
    f32::conv(0x01FF_FFFFu64);
}