| // Adapted from https://github.com/Alexhuszagh/rust-lexical. |
| |
| use crate::lexical::float::ExtendedFloat; |
| use crate::lexical::num::Float; |
| use crate::lexical::rounding::*; |
| |
| // MASKS |
| |
| #[test] |
| fn lower_n_mask_test() { |
| assert_eq!(lower_n_mask(0u64), 0b0); |
| assert_eq!(lower_n_mask(1u64), 0b1); |
| assert_eq!(lower_n_mask(2u64), 0b11); |
| assert_eq!(lower_n_mask(10u64), 0b1111111111); |
| assert_eq!(lower_n_mask(32u64), 0b11111111111111111111111111111111); |
| } |
| |
| #[test] |
| fn lower_n_halfway_test() { |
| assert_eq!(lower_n_halfway(0u64), 0b0); |
| assert_eq!(lower_n_halfway(1u64), 0b1); |
| assert_eq!(lower_n_halfway(2u64), 0b10); |
| assert_eq!(lower_n_halfway(10u64), 0b1000000000); |
| assert_eq!(lower_n_halfway(32u64), 0b10000000000000000000000000000000); |
| } |
| |
| #[test] |
| fn nth_bit_test() { |
| assert_eq!(nth_bit(0u64), 0b1); |
| assert_eq!(nth_bit(1u64), 0b10); |
| assert_eq!(nth_bit(2u64), 0b100); |
| assert_eq!(nth_bit(10u64), 0b10000000000); |
| assert_eq!(nth_bit(31u64), 0b10000000000000000000000000000000); |
| } |
| |
| #[test] |
| fn internal_n_mask_test() { |
| assert_eq!(internal_n_mask(1u64, 0u64), 0b0); |
| assert_eq!(internal_n_mask(1u64, 1u64), 0b1); |
| assert_eq!(internal_n_mask(2u64, 1u64), 0b10); |
| assert_eq!(internal_n_mask(4u64, 2u64), 0b1100); |
| assert_eq!(internal_n_mask(10u64, 2u64), 0b1100000000); |
| assert_eq!(internal_n_mask(10u64, 4u64), 0b1111000000); |
| assert_eq!( |
| internal_n_mask(32u64, 4u64), |
| 0b11110000000000000000000000000000 |
| ); |
| } |
| |
| // NEAREST ROUNDING |
| |
| #[test] |
| fn round_nearest_test() { |
| // Check exactly halfway (b'1100000') |
| let mut fp = ExtendedFloat { mant: 0x60, exp: 0 }; |
| let (above, halfway) = round_nearest(&mut fp, 6); |
| assert!(!above); |
| assert!(halfway); |
| assert_eq!(fp.mant, 1); |
| |
| // Check above halfway (b'1100001') |
| let mut fp = ExtendedFloat { mant: 0x61, exp: 0 }; |
| let (above, halfway) = round_nearest(&mut fp, 6); |
| assert!(above); |
| assert!(!halfway); |
| assert_eq!(fp.mant, 1); |
| |
| // Check below halfway (b'1011111') |
| let mut fp = ExtendedFloat { mant: 0x5F, exp: 0 }; |
| let (above, halfway) = round_nearest(&mut fp, 6); |
| assert!(!above); |
| assert!(!halfway); |
| assert_eq!(fp.mant, 1); |
| } |
| |
| // DIRECTED ROUNDING |
| |
| #[test] |
| fn round_downward_test() { |
| // b0000000 |
| let mut fp = ExtendedFloat { mant: 0x00, exp: 0 }; |
| round_downward(&mut fp, 6); |
| assert_eq!(fp.mant, 0); |
| |
| // b1000000 |
| let mut fp = ExtendedFloat { mant: 0x40, exp: 0 }; |
| round_downward(&mut fp, 6); |
| assert_eq!(fp.mant, 1); |
| |
| // b1100000 |
| let mut fp = ExtendedFloat { mant: 0x60, exp: 0 }; |
| round_downward(&mut fp, 6); |
| assert_eq!(fp.mant, 1); |
| |
| // b1110000 |
| let mut fp = ExtendedFloat { mant: 0x70, exp: 0 }; |
| round_downward(&mut fp, 6); |
| assert_eq!(fp.mant, 1); |
| } |
| |
| #[test] |
| fn round_nearest_tie_even_test() { |
| // Check round-up, halfway |
| let mut fp = ExtendedFloat { mant: 0x60, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 2); |
| |
| // Check round-down, halfway |
| let mut fp = ExtendedFloat { mant: 0x20, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 0); |
| |
| // Check round-up, above halfway |
| let mut fp = ExtendedFloat { mant: 0x61, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 2); |
| |
| let mut fp = ExtendedFloat { mant: 0x21, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 1); |
| |
| // Check round-down, below halfway |
| let mut fp = ExtendedFloat { mant: 0x5F, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 1); |
| |
| let mut fp = ExtendedFloat { mant: 0x1F, exp: 0 }; |
| round_nearest_tie_even(&mut fp, 6); |
| assert_eq!(fp.mant, 0); |
| } |
| |
| // HIGH-LEVEL |
| |
| #[test] |
| fn round_to_float_test() { |
| // Denormal |
| let mut fp = ExtendedFloat { |
| mant: 1 << 63, |
| exp: f64::DENORMAL_EXPONENT - 15, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 48); |
| assert_eq!(fp.exp, f64::DENORMAL_EXPONENT); |
| |
| // Halfway, round-down (b'1000000000000000000000000000000000000000000000000000010000000000') |
| let mut fp = ExtendedFloat { |
| mant: 0x8000000000000400, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 52); |
| assert_eq!(fp.exp, -52); |
| |
| // Halfway, round-up (b'1000000000000000000000000000000000000000000000000000110000000000') |
| let mut fp = ExtendedFloat { |
| mant: 0x8000000000000C00, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 2); |
| assert_eq!(fp.exp, -52); |
| |
| // Above halfway |
| let mut fp = ExtendedFloat { |
| mant: 0x8000000000000401, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 1); |
| assert_eq!(fp.exp, -52); |
| |
| let mut fp = ExtendedFloat { |
| mant: 0x8000000000000C01, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 2); |
| assert_eq!(fp.exp, -52); |
| |
| // Below halfway |
| let mut fp = ExtendedFloat { |
| mant: 0x80000000000003FF, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 52); |
| assert_eq!(fp.exp, -52); |
| |
| let mut fp = ExtendedFloat { |
| mant: 0x8000000000000BFF, |
| exp: -63, |
| }; |
| round_to_float::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 1); |
| assert_eq!(fp.exp, -52); |
| } |
| |
| #[test] |
| fn avoid_overflow_test() { |
| // Avoid overflow, fails by 1 |
| let mut fp = ExtendedFloat { |
| mant: 0xFFFFFFFFFFFF, |
| exp: f64::MAX_EXPONENT + 5, |
| }; |
| avoid_overflow::<f64>(&mut fp); |
| assert_eq!(fp.mant, 0xFFFFFFFFFFFF); |
| assert_eq!(fp.exp, f64::MAX_EXPONENT + 5); |
| |
| // Avoid overflow, succeeds |
| let mut fp = ExtendedFloat { |
| mant: 0xFFFFFFFFFFFF, |
| exp: f64::MAX_EXPONENT + 4, |
| }; |
| avoid_overflow::<f64>(&mut fp); |
| assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0); |
| assert_eq!(fp.exp, f64::MAX_EXPONENT - 1); |
| } |
| |
| #[test] |
| fn round_to_native_test() { |
| // Overflow |
| let mut fp = ExtendedFloat { |
| mant: 0xFFFFFFFFFFFF, |
| exp: f64::MAX_EXPONENT + 4, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0); |
| assert_eq!(fp.exp, f64::MAX_EXPONENT - 1); |
| |
| // Need denormal |
| let mut fp = ExtendedFloat { |
| mant: 1, |
| exp: f64::DENORMAL_EXPONENT + 48, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 48); |
| assert_eq!(fp.exp, f64::DENORMAL_EXPONENT); |
| |
| // Halfway, round-down (b'10000000000000000000000000000000000000000000000000000100000') |
| let mut fp = ExtendedFloat { |
| mant: 0x400000000000020, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 52); |
| assert_eq!(fp.exp, -52); |
| |
| // Halfway, round-up (b'10000000000000000000000000000000000000000000000000001100000') |
| let mut fp = ExtendedFloat { |
| mant: 0x400000000000060, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 2); |
| assert_eq!(fp.exp, -52); |
| |
| // Above halfway |
| let mut fp = ExtendedFloat { |
| mant: 0x400000000000021, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 1); |
| assert_eq!(fp.exp, -52); |
| |
| let mut fp = ExtendedFloat { |
| mant: 0x400000000000061, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 2); |
| assert_eq!(fp.exp, -52); |
| |
| // Below halfway |
| let mut fp = ExtendedFloat { |
| mant: 0x40000000000001F, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1 << 52); |
| assert_eq!(fp.exp, -52); |
| |
| let mut fp = ExtendedFloat { |
| mant: 0x40000000000005F, |
| exp: -58, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, (1 << 52) + 1); |
| assert_eq!(fp.exp, -52); |
| |
| // Underflow |
| // Adapted from failures in strtod. |
| let mut fp = ExtendedFloat { |
| exp: -1139, |
| mant: 18446744073709550712, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 0); |
| assert_eq!(fp.exp, 0); |
| |
| let mut fp = ExtendedFloat { |
| exp: -1139, |
| mant: 18446744073709551460, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 0); |
| assert_eq!(fp.exp, 0); |
| |
| let mut fp = ExtendedFloat { |
| exp: -1138, |
| mant: 9223372036854776103, |
| }; |
| round_to_native::<f64, _>(&mut fp, round_nearest_tie_even); |
| assert_eq!(fp.mant, 1); |
| assert_eq!(fp.exp, -1074); |
| } |