| // Adapted from https://github.com/Alexhuszagh/rust-lexical. |
| |
| use crate::lexical::float::ExtendedFloat; |
| use crate::lexical::rounding::round_nearest_tie_even; |
| use std::{f32, f64}; |
| |
| // NORMALIZE |
| |
| fn check_normalize(mant: u64, exp: i32, shift: u32, r_mant: u64, r_exp: i32) { |
| let mut x = ExtendedFloat { mant, exp }; |
| assert_eq!(x.normalize(), shift); |
| assert_eq!( |
| x, |
| ExtendedFloat { |
| mant: r_mant, |
| exp: r_exp |
| } |
| ); |
| } |
| |
| #[test] |
| fn normalize_test() { |
| // F32 |
| // 0 |
| check_normalize(0, 0, 0, 0, 0); |
| |
| // min value |
| check_normalize(1, -149, 63, 9223372036854775808, -212); |
| |
| // 1.0e-40 |
| check_normalize(71362, -149, 47, 10043308644012916736, -196); |
| |
| // 1.0e-20 |
| check_normalize(12379400, -90, 40, 13611294244890214400, -130); |
| |
| // 1.0 |
| check_normalize(8388608, -23, 40, 9223372036854775808, -63); |
| |
| // 1e20 |
| check_normalize(11368684, 43, 40, 12500000250510966784, 3); |
| |
| // max value |
| check_normalize(16777213, 104, 40, 18446740775174668288, 64); |
| |
| // F64 |
| |
| // min value |
| check_normalize(1, -1074, 63, 9223372036854775808, -1137); |
| |
| // 1.0e-250 |
| check_normalize(6448907850777164, -883, 11, 13207363278391631872, -894); |
| |
| // 1.0e-150 |
| check_normalize(7371020360979573, -551, 11, 15095849699286165504, -562); |
| |
| // 1.0e-45 |
| check_normalize(6427752177035961, -202, 11, 13164036458569648128, -213); |
| |
| // 1.0e-40 |
| check_normalize(4903985730770844, -185, 11, 10043362776618688512, -196); |
| |
| // 1.0e-20 |
| check_normalize(6646139978924579, -119, 11, 13611294676837537792, -130); |
| |
| // 1.0 |
| check_normalize(4503599627370496, -52, 11, 9223372036854775808, -63); |
| |
| // 1e20 |
| check_normalize(6103515625000000, 14, 11, 12500000000000000000, 3); |
| |
| // 1e40 |
| check_normalize(8271806125530277, 80, 11, 16940658945086007296, 69); |
| |
| // 1e150 |
| check_normalize(5503284107318959, 446, 11, 11270725851789228032, 435); |
| |
| // 1e250 |
| check_normalize(6290184345309700, 778, 11, 12882297539194265600, 767); |
| |
| // max value |
| check_normalize(9007199254740991, 971, 11, 18446744073709549568, 960); |
| } |
| |
| // ROUND |
| |
| fn check_round_to_f32(mant: u64, exp: i32, r_mant: u64, r_exp: i32) { |
| let mut x = ExtendedFloat { mant, exp }; |
| x.round_to_native::<f32, _>(round_nearest_tie_even); |
| assert_eq!( |
| x, |
| ExtendedFloat { |
| mant: r_mant, |
| exp: r_exp |
| } |
| ); |
| } |
| |
| #[test] |
| fn round_to_f32_test() { |
| // This is lossy, so some of these values are **slightly** rounded. |
| |
| // underflow |
| check_round_to_f32(9223372036854775808, -213, 0, -149); |
| |
| // min value |
| check_round_to_f32(9223372036854775808, -212, 1, -149); |
| |
| // 1.0e-40 |
| check_round_to_f32(10043308644012916736, -196, 71362, -149); |
| |
| // 1.0e-20 |
| check_round_to_f32(13611294244890214400, -130, 12379400, -90); |
| |
| // 1.0 |
| check_round_to_f32(9223372036854775808, -63, 8388608, -23); |
| |
| // 1e20 |
| check_round_to_f32(12500000250510966784, 3, 11368684, 43); |
| |
| // max value |
| check_round_to_f32(18446740775174668288, 64, 16777213, 104); |
| |
| // overflow |
| check_round_to_f32(18446740775174668288, 65, 16777213, 105); |
| } |
| |
| fn check_round_to_f64(mant: u64, exp: i32, r_mant: u64, r_exp: i32) { |
| let mut x = ExtendedFloat { mant, exp }; |
| x.round_to_native::<f64, _>(round_nearest_tie_even); |
| assert_eq!( |
| x, |
| ExtendedFloat { |
| mant: r_mant, |
| exp: r_exp |
| } |
| ); |
| } |
| |
| #[test] |
| fn round_to_f64_test() { |
| // This is lossy, so some of these values are **slightly** rounded. |
| |
| // underflow |
| check_round_to_f64(9223372036854775808, -1138, 0, -1074); |
| |
| // min value |
| check_round_to_f64(9223372036854775808, -1137, 1, -1074); |
| |
| // 1.0e-250 |
| check_round_to_f64(15095849699286165504, -562, 7371020360979573, -551); |
| |
| // 1.0e-150 |
| check_round_to_f64(15095849699286165504, -562, 7371020360979573, -551); |
| |
| // 1.0e-45 |
| check_round_to_f64(13164036458569648128, -213, 6427752177035961, -202); |
| |
| // 1.0e-40 |
| check_round_to_f64(10043362776618688512, -196, 4903985730770844, -185); |
| |
| // 1.0e-20 |
| check_round_to_f64(13611294676837537792, -130, 6646139978924579, -119); |
| |
| // 1.0 |
| check_round_to_f64(9223372036854775808, -63, 4503599627370496, -52); |
| |
| // 1e20 |
| check_round_to_f64(12500000000000000000, 3, 6103515625000000, 14); |
| |
| // 1e40 |
| check_round_to_f64(16940658945086007296, 69, 8271806125530277, 80); |
| |
| // 1e150 |
| check_round_to_f64(11270725851789228032, 435, 5503284107318959, 446); |
| |
| // 1e250 |
| check_round_to_f64(12882297539194265600, 767, 6290184345309700, 778); |
| |
| // max value |
| check_round_to_f64(18446744073709549568, 960, 9007199254740991, 971); |
| |
| // Bug fixes |
| // 1.2345e-308 |
| check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074); |
| } |
| |
| fn assert_normalized_eq(mut x: ExtendedFloat, mut y: ExtendedFloat) { |
| x.normalize(); |
| y.normalize(); |
| assert_eq!(x, y); |
| } |
| |
| #[test] |
| fn from_float() { |
| let values: [f32; 26] = [ |
| 1e-40, 2e-40, 1e-35, 2e-35, 1e-30, 2e-30, 1e-25, 2e-25, 1e-20, 2e-20, 1e-15, 2e-15, 1e-10, |
| 2e-10, 1e-5, 2e-5, 1.0, 2.0, 1e5, 2e5, 1e10, 2e10, 1e15, 2e15, 1e20, 2e20, |
| ]; |
| for value in &values { |
| assert_normalized_eq( |
| ExtendedFloat::from_float(*value), |
| ExtendedFloat::from_float(*value as f64), |
| ); |
| } |
| } |
| |
| // TO |
| |
| // Sample of interesting numbers to check during standard test builds. |
| const INTEGERS: [u64; 32] = [ |
| 0, // 0x0 |
| 1, // 0x1 |
| 7, // 0x7 |
| 15, // 0xF |
| 112, // 0x70 |
| 119, // 0x77 |
| 127, // 0x7F |
| 240, // 0xF0 |
| 247, // 0xF7 |
| 255, // 0xFF |
| 2032, // 0x7F0 |
| 2039, // 0x7F7 |
| 2047, // 0x7FF |
| 4080, // 0xFF0 |
| 4087, // 0xFF7 |
| 4095, // 0xFFF |
| 65520, // 0xFFF0 |
| 65527, // 0xFFF7 |
| 65535, // 0xFFFF |
| 1048560, // 0xFFFF0 |
| 1048567, // 0xFFFF7 |
| 1048575, // 0xFFFFF |
| 16777200, // 0xFFFFF0 |
| 16777207, // 0xFFFFF7 |
| 16777215, // 0xFFFFFF |
| 268435440, // 0xFFFFFF0 |
| 268435447, // 0xFFFFFF7 |
| 268435455, // 0xFFFFFFF |
| 4294967280, // 0xFFFFFFF0 |
| 4294967287, // 0xFFFFFFF7 |
| 4294967295, // 0xFFFFFFFF |
| 18446744073709551615, // 0xFFFFFFFFFFFFFFFF |
| ]; |
| |
| #[test] |
| fn to_f32_test() { |
| // underflow |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -213, |
| }; |
| assert_eq!(x.into_float::<f32>(), 0.0); |
| |
| // min value |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -212, |
| }; |
| assert_eq!(x.into_float::<f32>(), 1e-45); |
| |
| // 1.0e-40 |
| let x = ExtendedFloat { |
| mant: 10043308644012916736, |
| exp: -196, |
| }; |
| assert_eq!(x.into_float::<f32>(), 1e-40); |
| |
| // 1.0e-20 |
| let x = ExtendedFloat { |
| mant: 13611294244890214400, |
| exp: -130, |
| }; |
| assert_eq!(x.into_float::<f32>(), 1e-20); |
| |
| // 1.0 |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -63, |
| }; |
| assert_eq!(x.into_float::<f32>(), 1.0); |
| |
| // 1e20 |
| let x = ExtendedFloat { |
| mant: 12500000250510966784, |
| exp: 3, |
| }; |
| assert_eq!(x.into_float::<f32>(), 1e20); |
| |
| // max value |
| let x = ExtendedFloat { |
| mant: 18446740775174668288, |
| exp: 64, |
| }; |
| assert_eq!(x.into_float::<f32>(), 3.402823e38); |
| |
| // almost max, high exp |
| let x = ExtendedFloat { |
| mant: 1048575, |
| exp: 108, |
| }; |
| assert_eq!(x.into_float::<f32>(), 3.4028204e38); |
| |
| // max value + 1 |
| let x = ExtendedFloat { |
| mant: 16777216, |
| exp: 104, |
| }; |
| assert_eq!(x.into_float::<f32>(), f32::INFINITY); |
| |
| // max value + 1 |
| let x = ExtendedFloat { |
| mant: 1048576, |
| exp: 108, |
| }; |
| assert_eq!(x.into_float::<f32>(), f32::INFINITY); |
| |
| // 1e40 |
| let x = ExtendedFloat { |
| mant: 16940658945086007296, |
| exp: 69, |
| }; |
| assert_eq!(x.into_float::<f32>(), f32::INFINITY); |
| |
| // Integers. |
| for int in &INTEGERS { |
| let fp = ExtendedFloat { mant: *int, exp: 0 }; |
| assert_eq!(fp.into_float::<f32>(), *int as f32, "{:?} as f32", *int); |
| } |
| } |
| |
| #[test] |
| fn to_f64_test() { |
| // underflow |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -1138, |
| }; |
| assert_eq!(x.into_float::<f64>(), 0.0); |
| |
| // min value |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -1137, |
| }; |
| assert_eq!(x.into_float::<f64>(), 5e-324); |
| |
| // 1.0e-250 |
| let x = ExtendedFloat { |
| mant: 13207363278391631872, |
| exp: -894, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e-250); |
| |
| // 1.0e-150 |
| let x = ExtendedFloat { |
| mant: 15095849699286165504, |
| exp: -562, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e-150); |
| |
| // 1.0e-45 |
| let x = ExtendedFloat { |
| mant: 13164036458569648128, |
| exp: -213, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e-45); |
| |
| // 1.0e-40 |
| let x = ExtendedFloat { |
| mant: 10043362776618688512, |
| exp: -196, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e-40); |
| |
| // 1.0e-20 |
| let x = ExtendedFloat { |
| mant: 13611294676837537792, |
| exp: -130, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e-20); |
| |
| // 1.0 |
| let x = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -63, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1.0); |
| |
| // 1e20 |
| let x = ExtendedFloat { |
| mant: 12500000000000000000, |
| exp: 3, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e20); |
| |
| // 1e40 |
| let x = ExtendedFloat { |
| mant: 16940658945086007296, |
| exp: 69, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e40); |
| |
| // 1e150 |
| let x = ExtendedFloat { |
| mant: 11270725851789228032, |
| exp: 435, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e150); |
| |
| // 1e250 |
| let x = ExtendedFloat { |
| mant: 12882297539194265600, |
| exp: 767, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1e250); |
| |
| // max value |
| let x = ExtendedFloat { |
| mant: 9007199254740991, |
| exp: 971, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1.7976931348623157e308); |
| |
| // max value |
| let x = ExtendedFloat { |
| mant: 18446744073709549568, |
| exp: 960, |
| }; |
| assert_eq!(x.into_float::<f64>(), 1.7976931348623157e308); |
| |
| // overflow |
| let x = ExtendedFloat { |
| mant: 9007199254740992, |
| exp: 971, |
| }; |
| assert_eq!(x.into_float::<f64>(), f64::INFINITY); |
| |
| // overflow |
| let x = ExtendedFloat { |
| mant: 18446744073709549568, |
| exp: 961, |
| }; |
| assert_eq!(x.into_float::<f64>(), f64::INFINITY); |
| |
| // Underflow |
| // Adapted from failures in strtod. |
| let x = ExtendedFloat { |
| exp: -1139, |
| mant: 18446744073709550712, |
| }; |
| assert_eq!(x.into_float::<f64>(), 0.0); |
| |
| let x = ExtendedFloat { |
| exp: -1139, |
| mant: 18446744073709551460, |
| }; |
| assert_eq!(x.into_float::<f64>(), 0.0); |
| |
| let x = ExtendedFloat { |
| exp: -1138, |
| mant: 9223372036854776103, |
| }; |
| assert_eq!(x.into_float::<f64>(), 5e-324); |
| |
| // Integers. |
| for int in &INTEGERS { |
| let fp = ExtendedFloat { mant: *int, exp: 0 }; |
| assert_eq!(fp.into_float::<f64>(), *int as f64, "{:?} as f64", *int); |
| } |
| } |
| |
| // OPERATIONS |
| |
| fn check_mul(a: ExtendedFloat, b: ExtendedFloat, c: ExtendedFloat) { |
| let r = a.mul(&b); |
| assert_eq!(r, c); |
| } |
| |
| #[test] |
| fn mul_test() { |
| // Normalized (64-bit mantissa) |
| let a = ExtendedFloat { |
| mant: 13164036458569648128, |
| exp: -213, |
| }; |
| let b = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -62, |
| }; |
| let c = ExtendedFloat { |
| mant: 6582018229284824064, |
| exp: -211, |
| }; |
| check_mul(a, b, c); |
| |
| // Check with integers |
| // 64-bit mantissa |
| let mut a = ExtendedFloat { mant: 10, exp: 0 }; |
| let mut b = ExtendedFloat { mant: 10, exp: 0 }; |
| a.normalize(); |
| b.normalize(); |
| assert_eq!(a.mul(&b).into_float::<f64>(), 100.0); |
| |
| // Check both values need high bits set. |
| let a = ExtendedFloat { |
| mant: 1 << 32, |
| exp: -31, |
| }; |
| let b = ExtendedFloat { |
| mant: 1 << 32, |
| exp: -31, |
| }; |
| assert_eq!(a.mul(&b).into_float::<f64>(), 4.0); |
| |
| // Check both values need high bits set. |
| let a = ExtendedFloat { |
| mant: 10 << 31, |
| exp: -31, |
| }; |
| let b = ExtendedFloat { |
| mant: 10 << 31, |
| exp: -31, |
| }; |
| assert_eq!(a.mul(&b).into_float::<f64>(), 100.0); |
| } |
| |
| fn check_imul(mut a: ExtendedFloat, b: ExtendedFloat, c: ExtendedFloat) { |
| a.imul(&b); |
| assert_eq!(a, c); |
| } |
| |
| #[test] |
| fn imul_test() { |
| // Normalized (64-bit mantissa) |
| let a = ExtendedFloat { |
| mant: 13164036458569648128, |
| exp: -213, |
| }; |
| let b = ExtendedFloat { |
| mant: 9223372036854775808, |
| exp: -62, |
| }; |
| let c = ExtendedFloat { |
| mant: 6582018229284824064, |
| exp: -211, |
| }; |
| check_imul(a, b, c); |
| |
| // Check with integers |
| // 64-bit mantissa |
| let mut a = ExtendedFloat { mant: 10, exp: 0 }; |
| let mut b = ExtendedFloat { mant: 10, exp: 0 }; |
| a.normalize(); |
| b.normalize(); |
| a.imul(&b); |
| assert_eq!(a.into_float::<f64>(), 100.0); |
| |
| // Check both values need high bits set. |
| let mut a = ExtendedFloat { |
| mant: 1 << 32, |
| exp: -31, |
| }; |
| let b = ExtendedFloat { |
| mant: 1 << 32, |
| exp: -31, |
| }; |
| a.imul(&b); |
| assert_eq!(a.into_float::<f64>(), 4.0); |
| |
| // Check both values need high bits set. |
| let mut a = ExtendedFloat { |
| mant: 10 << 31, |
| exp: -31, |
| }; |
| let b = ExtendedFloat { |
| mant: 10 << 31, |
| exp: -31, |
| }; |
| a.imul(&b); |
| assert_eq!(a.into_float::<f64>(), 100.0); |
| } |