summaryrefslogtreecommitdiffstats
path: root/library/core/tests/num
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /library/core/tests/num
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/core/tests/num')
-rw-r--r--library/core/tests/num/bignum.rs276
-rw-r--r--library/core/tests/num/const_from.rs25
-rw-r--r--library/core/tests/num/dec2flt/float.rs33
-rw-r--r--library/core/tests/num/dec2flt/lemire.rs53
-rw-r--r--library/core/tests/num/dec2flt/mod.rs140
-rw-r--r--library/core/tests/num/dec2flt/parse.rs177
-rw-r--r--library/core/tests/num/flt2dec/estimator.rs62
-rw-r--r--library/core/tests/num/flt2dec/mod.rs1172
-rw-r--r--library/core/tests/num/flt2dec/random.rs202
-rw-r--r--library/core/tests/num/flt2dec/strategy/dragon.rs63
-rw-r--r--library/core/tests/num/flt2dec/strategy/grisu.rs72
-rw-r--r--library/core/tests/num/i128.rs1
-rw-r--r--library/core/tests/num/i16.rs1
-rw-r--r--library/core/tests/num/i32.rs30
-rw-r--r--library/core/tests/num/i64.rs1
-rw-r--r--library/core/tests/num/i8.rs1
-rw-r--r--library/core/tests/num/ieee754.rs158
-rw-r--r--library/core/tests/num/int_log.rs166
-rw-r--r--library/core/tests/num/int_macros.rs343
-rw-r--r--library/core/tests/num/mod.rs871
-rw-r--r--library/core/tests/num/nan.rs7
-rw-r--r--library/core/tests/num/ops.rs232
-rw-r--r--library/core/tests/num/u128.rs1
-rw-r--r--library/core/tests/num/u16.rs1
-rw-r--r--library/core/tests/num/u32.rs1
-rw-r--r--library/core/tests/num/u64.rs1
-rw-r--r--library/core/tests/num/u8.rs1
-rw-r--r--library/core/tests/num/uint_macros.rs235
-rw-r--r--library/core/tests/num/wrapping.rs320
29 files changed, 4646 insertions, 0 deletions
diff --git a/library/core/tests/num/bignum.rs b/library/core/tests/num/bignum.rs
new file mode 100644
index 000000000..416e7cea7
--- /dev/null
+++ b/library/core/tests/num/bignum.rs
@@ -0,0 +1,276 @@
+use core::num::bignum::tests::Big8x3 as Big;
+use core::num::bignum::Big32x40;
+
+#[test]
+#[should_panic]
+fn test_from_u64_overflow() {
+ Big::from_u64(0x1000000);
+}
+
+#[test]
+fn test_add() {
+ assert_eq!(*Big::from_small(3).add(&Big::from_small(4)), Big::from_small(7));
+ assert_eq!(*Big::from_small(3).add(&Big::from_small(0)), Big::from_small(3));
+ assert_eq!(*Big::from_small(0).add(&Big::from_small(3)), Big::from_small(3));
+ assert_eq!(*Big::from_small(3).add(&Big::from_u64(0xfffe)), Big::from_u64(0x10001));
+ assert_eq!(*Big::from_u64(0xfedc).add(&Big::from_u64(0x789)), Big::from_u64(0x10665));
+ assert_eq!(*Big::from_u64(0x789).add(&Big::from_u64(0xfedc)), Big::from_u64(0x10665));
+}
+
+#[test]
+#[should_panic]
+fn test_add_overflow_1() {
+ Big::from_small(1).add(&Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_add_overflow_2() {
+ Big::from_u64(0xffffff).add(&Big::from_small(1));
+}
+
+#[test]
+fn test_add_small() {
+ assert_eq!(*Big::from_small(3).add_small(4), Big::from_small(7));
+ assert_eq!(*Big::from_small(3).add_small(0), Big::from_small(3));
+ assert_eq!(*Big::from_small(0).add_small(3), Big::from_small(3));
+ assert_eq!(*Big::from_small(7).add_small(250), Big::from_u64(257));
+ assert_eq!(*Big::from_u64(0x7fff).add_small(1), Big::from_u64(0x8000));
+ assert_eq!(*Big::from_u64(0x2ffe).add_small(0x35), Big::from_u64(0x3033));
+ assert_eq!(*Big::from_small(0xdc).add_small(0x89), Big::from_u64(0x165));
+}
+
+#[test]
+#[should_panic]
+fn test_add_small_overflow() {
+ Big::from_u64(0xffffff).add_small(1);
+}
+
+#[test]
+fn test_sub() {
+ assert_eq!(*Big::from_small(7).sub(&Big::from_small(4)), Big::from_small(3));
+ assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x789)), Big::from_u64(0xfedc));
+ assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0xfedc)), Big::from_u64(0x789));
+ assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10664)), Big::from_small(1));
+ assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10665)), Big::from_small(0));
+}
+
+#[test]
+#[should_panic]
+fn test_sub_underflow_1() {
+ Big::from_u64(0x10665).sub(&Big::from_u64(0x10666));
+}
+
+#[test]
+#[should_panic]
+fn test_sub_underflow_2() {
+ Big::from_small(0).sub(&Big::from_u64(0x123456));
+}
+
+#[test]
+fn test_mul_small() {
+ assert_eq!(*Big::from_small(7).mul_small(5), Big::from_small(35));
+ assert_eq!(*Big::from_small(0xff).mul_small(0xff), Big::from_u64(0xfe01));
+ assert_eq!(*Big::from_u64(0xffffff / 13).mul_small(13), Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_small_overflow() {
+ Big::from_u64(0x800000).mul_small(2);
+}
+
+#[test]
+fn test_mul_pow2() {
+ assert_eq!(*Big::from_small(0x7).mul_pow2(4), Big::from_small(0x70));
+ assert_eq!(*Big::from_small(0xff).mul_pow2(1), Big::from_u64(0x1fe));
+ assert_eq!(*Big::from_small(0xff).mul_pow2(12), Big::from_u64(0xff000));
+ assert_eq!(*Big::from_small(0x1).mul_pow2(23), Big::from_u64(0x800000));
+ assert_eq!(*Big::from_u64(0x123).mul_pow2(0), Big::from_u64(0x123));
+ assert_eq!(*Big::from_u64(0x123).mul_pow2(7), Big::from_u64(0x9180));
+ assert_eq!(*Big::from_u64(0x123).mul_pow2(15), Big::from_u64(0x918000));
+ assert_eq!(*Big::from_small(0).mul_pow2(23), Big::from_small(0));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow2_overflow_1() {
+ Big::from_u64(0x1).mul_pow2(24);
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow2_overflow_2() {
+ Big::from_u64(0x123).mul_pow2(16);
+}
+
+#[test]
+fn test_mul_pow5() {
+ assert_eq!(*Big::from_small(42).mul_pow5(0), Big::from_small(42));
+ assert_eq!(*Big::from_small(1).mul_pow5(2), Big::from_small(25));
+ assert_eq!(*Big::from_small(1).mul_pow5(4), Big::from_u64(25 * 25));
+ assert_eq!(*Big::from_small(4).mul_pow5(3), Big::from_u64(500));
+ assert_eq!(*Big::from_small(140).mul_pow5(2), Big::from_u64(25 * 140));
+ assert_eq!(*Big::from_small(25).mul_pow5(1), Big::from_small(125));
+ assert_eq!(*Big::from_small(125).mul_pow5(7), Big::from_u64(9765625));
+ assert_eq!(*Big::from_small(0).mul_pow5(127), Big::from_small(0));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow5_overflow_1() {
+ Big::from_small(1).mul_pow5(12);
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow5_overflow_2() {
+ Big::from_small(230).mul_pow5(8);
+}
+
+#[test]
+fn test_mul_digits() {
+ assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15));
+ assert_eq!(*Big::from_small(0xff).mul_digits(&[0xff]), Big::from_u64(0xfe01));
+ assert_eq!(*Big::from_u64(0x123).mul_digits(&[0x56, 0x4]), Big::from_u64(0x4edc2));
+ assert_eq!(*Big::from_u64(0x12345).mul_digits(&[0x67]), Big::from_u64(0x7530c3));
+ assert_eq!(*Big::from_small(0x12).mul_digits(&[0x67, 0x45, 0x3]), Big::from_u64(0x3ae13e));
+ assert_eq!(*Big::from_u64(0xffffff / 13).mul_digits(&[13]), Big::from_u64(0xffffff));
+ assert_eq!(*Big::from_small(13).mul_digits(&[0x3b, 0xb1, 0x13]), Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_digits_overflow_1() {
+ Big::from_u64(0x800000).mul_digits(&[2]);
+}
+
+#[test]
+#[should_panic]
+fn test_mul_digits_overflow_2() {
+ Big::from_u64(0x1000).mul_digits(&[0, 0x10]);
+}
+
+#[test]
+fn test_div_rem_small() {
+ let as_val = |(q, r): (&mut Big, u8)| (q.clone(), r);
+ assert_eq!(as_val(Big::from_small(0xff).div_rem_small(15)), (Big::from_small(17), 0));
+ assert_eq!(as_val(Big::from_small(0xff).div_rem_small(16)), (Big::from_small(15), 15));
+ assert_eq!(as_val(Big::from_small(3).div_rem_small(40)), (Big::from_small(0), 3));
+ assert_eq!(
+ as_val(Big::from_u64(0xffffff).div_rem_small(123)),
+ (Big::from_u64(0xffffff / 123), (0xffffffu64 % 123) as u8)
+ );
+ assert_eq!(
+ as_val(Big::from_u64(0x10000).div_rem_small(123)),
+ (Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8)
+ );
+}
+
+#[test]
+fn test_div_rem() {
+ fn div_rem(n: u64, d: u64) -> (Big, Big) {
+ let mut q = Big::from_small(42);
+ let mut r = Big::from_small(42);
+ Big::from_u64(n).div_rem(&Big::from_u64(d), &mut q, &mut r);
+ (q, r)
+ }
+ assert_eq!(div_rem(1, 1), (Big::from_small(1), Big::from_small(0)));
+ assert_eq!(div_rem(4, 3), (Big::from_small(1), Big::from_small(1)));
+ assert_eq!(div_rem(1, 7), (Big::from_small(0), Big::from_small(1)));
+ assert_eq!(div_rem(45, 9), (Big::from_small(5), Big::from_small(0)));
+ assert_eq!(div_rem(103, 9), (Big::from_small(11), Big::from_small(4)));
+ assert_eq!(div_rem(123456, 77), (Big::from_u64(1603), Big::from_small(25)));
+ assert_eq!(div_rem(0xffff, 1), (Big::from_u64(0xffff), Big::from_small(0)));
+ assert_eq!(div_rem(0xeeee, 0xffff), (Big::from_small(0), Big::from_u64(0xeeee)));
+ assert_eq!(div_rem(2_000_000, 2), (Big::from_u64(1_000_000), Big::from_u64(0)));
+}
+
+#[test]
+fn test_is_zero() {
+ assert!(Big::from_small(0).is_zero());
+ assert!(!Big::from_small(3).is_zero());
+ assert!(!Big::from_u64(0x123).is_zero());
+ assert!(!Big::from_u64(0xffffff).sub(&Big::from_u64(0xfffffe)).is_zero());
+ assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero());
+}
+
+#[test]
+fn test_get_bit() {
+ let x = Big::from_small(0b1101);
+ assert_eq!(x.get_bit(0), 1);
+ assert_eq!(x.get_bit(1), 0);
+ assert_eq!(x.get_bit(2), 1);
+ assert_eq!(x.get_bit(3), 1);
+ let y = Big::from_u64(1 << 15);
+ assert_eq!(y.get_bit(14), 0);
+ assert_eq!(y.get_bit(15), 1);
+ assert_eq!(y.get_bit(16), 0);
+}
+
+#[test]
+#[should_panic]
+fn test_get_bit_out_of_range() {
+ Big::from_small(42).get_bit(24);
+}
+
+#[test]
+fn test_bit_length() {
+ for i in 0..8 * 3 {
+ // 010000...000
+ assert_eq!(Big::from_small(1).mul_pow2(i).bit_length(), i + 1);
+ }
+ for i in 1..8 * 3 - 1 {
+ // 010000...001
+ assert_eq!(Big::from_small(1).mul_pow2(i).add(&Big::from_small(1)).bit_length(), i + 1);
+ // 110000...000
+ assert_eq!(Big::from_small(3).mul_pow2(i).bit_length(), i + 2);
+ }
+ assert_eq!(Big::from_small(0).bit_length(), 0);
+ assert_eq!(Big::from_small(1).bit_length(), 1);
+ assert_eq!(Big::from_small(5).bit_length(), 3);
+ assert_eq!(Big::from_small(0x18).bit_length(), 5);
+ assert_eq!(Big::from_u64(0x4073).bit_length(), 15);
+ assert_eq!(Big::from_u64(0xffffff).bit_length(), 24);
+}
+
+#[test]
+fn test_bit_length_32x40() {
+ for i in 0..32 * 40 {
+ // 010000...000
+ assert_eq!(Big32x40::from_small(1).mul_pow2(i).bit_length(), i + 1);
+ }
+ for i in 1..32 * 40 - 1 {
+ // 010000...001
+ assert_eq!(
+ Big32x40::from_small(1).mul_pow2(i).add(&Big32x40::from_small(1)).bit_length(),
+ i + 1
+ );
+ // 110000...000
+ assert_eq!(Big32x40::from_small(3).mul_pow2(i).bit_length(), i + 2);
+ }
+ assert_eq!(Big32x40::from_small(0).bit_length(), 0);
+ assert_eq!(Big32x40::from_small(1).bit_length(), 1);
+ assert_eq!(Big32x40::from_small(5).bit_length(), 3);
+ assert_eq!(Big32x40::from_small(0x18).bit_length(), 5);
+ assert_eq!(Big32x40::from_u64(0x4073).bit_length(), 15);
+ assert_eq!(Big32x40::from_u64(0xffffff).bit_length(), 24);
+ assert_eq!(Big32x40::from_u64(0xffffffffffffffff).bit_length(), 64);
+}
+
+#[test]
+fn test_ord() {
+ assert!(Big::from_u64(0) < Big::from_u64(0xffffff));
+ assert!(Big::from_u64(0x102) < Big::from_u64(0x201));
+}
+
+#[test]
+fn test_fmt() {
+ assert_eq!(format!("{:?}", Big::from_u64(0)), "0x0");
+ assert_eq!(format!("{:?}", Big::from_u64(0x1)), "0x1");
+ assert_eq!(format!("{:?}", Big::from_u64(0x12)), "0x12");
+ assert_eq!(format!("{:?}", Big::from_u64(0x123)), "0x1_23");
+ assert_eq!(format!("{:?}", Big::from_u64(0x1234)), "0x12_34");
+ assert_eq!(format!("{:?}", Big::from_u64(0x12345)), "0x1_23_45");
+ assert_eq!(format!("{:?}", Big::from_u64(0x123456)), "0x12_34_56");
+}
diff --git a/library/core/tests/num/const_from.rs b/library/core/tests/num/const_from.rs
new file mode 100644
index 000000000..aca18ef39
--- /dev/null
+++ b/library/core/tests/num/const_from.rs
@@ -0,0 +1,25 @@
+#[test]
+fn from() {
+ use core::convert::TryFrom;
+ use core::num::TryFromIntError;
+
+ // From
+ const FROM: i64 = i64::from(1i32);
+ assert_eq!(FROM, 1i64);
+
+ // From int to float
+ const FROM_F64: f64 = f64::from(42u8);
+ assert_eq!(FROM_F64, 42f64);
+
+ // Upper bounded
+ const U8_FROM_U16: Result<u8, TryFromIntError> = u8::try_from(1u16);
+ assert_eq!(U8_FROM_U16, Ok(1u8));
+
+ // Both bounded
+ const I8_FROM_I16: Result<i8, TryFromIntError> = i8::try_from(1i16);
+ assert_eq!(I8_FROM_I16, Ok(1i8));
+
+ // Lower bounded
+ const I16_FROM_U16: Result<i16, TryFromIntError> = i16::try_from(1u16);
+ assert_eq!(I16_FROM_U16, Ok(1i16));
+}
diff --git a/library/core/tests/num/dec2flt/float.rs b/library/core/tests/num/dec2flt/float.rs
new file mode 100644
index 000000000..7a9587a18
--- /dev/null
+++ b/library/core/tests/num/dec2flt/float.rs
@@ -0,0 +1,33 @@
+use core::num::dec2flt::float::RawFloat;
+
+#[test]
+fn test_f32_integer_decode() {
+ assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
+ assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1));
+ assert_eq!(2f32.powf(100.0).integer_decode(), (8388608, 77, 1));
+ assert_eq!(0f32.integer_decode(), (0, -150, 1));
+ assert_eq!((-0f32).integer_decode(), (0, -150, -1));
+ assert_eq!(f32::INFINITY.integer_decode(), (8388608, 105, 1));
+ assert_eq!(f32::NEG_INFINITY.integer_decode(), (8388608, 105, -1));
+
+ // Ignore the "sign" (quiet / signalling flag) of NAN.
+ // It can vary between runtime operations and LLVM folding.
+ let (nan_m, nan_e, _nan_s) = f32::NAN.integer_decode();
+ assert_eq!((nan_m, nan_e), (12582912, 105));
+}
+
+#[test]
+fn test_f64_integer_decode() {
+ assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1));
+ assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1));
+ assert_eq!(2f64.powf(100.0).integer_decode(), (4503599627370496, 48, 1));
+ assert_eq!(0f64.integer_decode(), (0, -1075, 1));
+ assert_eq!((-0f64).integer_decode(), (0, -1075, -1));
+ assert_eq!(f64::INFINITY.integer_decode(), (4503599627370496, 972, 1));
+ assert_eq!(f64::NEG_INFINITY.integer_decode(), (4503599627370496, 972, -1));
+
+ // Ignore the "sign" (quiet / signalling flag) of NAN.
+ // It can vary between runtime operations and LLVM folding.
+ let (nan_m, nan_e, _nan_s) = f64::NAN.integer_decode();
+ assert_eq!((nan_m, nan_e), (6755399441055744, 972));
+}
diff --git a/library/core/tests/num/dec2flt/lemire.rs b/library/core/tests/num/dec2flt/lemire.rs
new file mode 100644
index 000000000..f71bbb7c7
--- /dev/null
+++ b/library/core/tests/num/dec2flt/lemire.rs
@@ -0,0 +1,53 @@
+use core::num::dec2flt::lemire::compute_float;
+
+fn compute_float32(q: i64, w: u64) -> (i32, u64) {
+ let fp = compute_float::<f32>(q, w);
+ (fp.e, fp.f)
+}
+
+fn compute_float64(q: i64, w: u64) -> (i32, u64) {
+ let fp = compute_float::<f64>(q, w);
+ (fp.e, fp.f)
+}
+
+#[test]
+fn compute_float_f32_rounding() {
+ // These test near-halfway cases for single-precision floats.
+ assert_eq!(compute_float32(0, 16777216), (151, 0));
+ assert_eq!(compute_float32(0, 16777217), (151, 0));
+ assert_eq!(compute_float32(0, 16777218), (151, 1));
+ assert_eq!(compute_float32(0, 16777219), (151, 2));
+ assert_eq!(compute_float32(0, 16777220), (151, 2));
+
+ // These are examples of the above tests, with
+ // digits from the exponent shifted to the mantissa.
+ assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
+ assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
+ assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
+ // Let's check the lines to see if anything is different in table...
+ assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
+ assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
+}
+
+#[test]
+fn compute_float_f64_rounding() {
+ // These test near-halfway cases for double-precision floats.
+ assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
+ assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
+ assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
+ assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
+ assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
+ assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
+ assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
+ assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
+ assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
+ assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
+
+ // These are examples of the above tests, with
+ // digits from the exponent shifted to the mantissa.
+ assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
+ assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
+ assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
+ assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
+ assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
+}
diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/core/tests/num/dec2flt/mod.rs
new file mode 100644
index 000000000..c4e105cba
--- /dev/null
+++ b/library/core/tests/num/dec2flt/mod.rs
@@ -0,0 +1,140 @@
+#![allow(overflowing_literals)]
+
+mod float;
+mod lemire;
+mod parse;
+
+// Take a float literal, turn it into a string in various ways (that are all trusted
+// to be correct) and see if those strings are parsed back to the value of the literal.
+// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
+macro_rules! test_literal {
+ ($x: expr) => {{
+ let x32: f32 = $x;
+ let x64: f64 = $x;
+ let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
+ for input in inputs {
+ assert_eq!(input.parse(), Ok(x64));
+ assert_eq!(input.parse(), Ok(x32));
+ let neg_input = &format!("-{input}");
+ assert_eq!(neg_input.parse(), Ok(-x64));
+ assert_eq!(neg_input.parse(), Ok(-x32));
+ }
+ }};
+}
+
+#[test]
+fn ordinary() {
+ test_literal!(1.0);
+ test_literal!(3e-5);
+ test_literal!(0.1);
+ test_literal!(12345.);
+ test_literal!(0.9999999);
+ test_literal!(2.2250738585072014e-308);
+}
+
+#[test]
+fn special_code_paths() {
+ test_literal!(36893488147419103229.0); // 2^65 - 3, triggers half-to-even with even significand
+ test_literal!(101e-33); // Triggers the tricky underflow case in AlgorithmM (for f32)
+ test_literal!(1e23); // Triggers AlgorithmR
+ test_literal!(2075e23); // Triggers another path through AlgorithmR
+ test_literal!(8713e-23); // ... and yet another.
+}
+
+#[test]
+fn large() {
+ test_literal!(1e300);
+ test_literal!(123456789.34567e250);
+ test_literal!(943794359898089732078308743689303290943794359843568973207830874368930329.);
+}
+
+#[test]
+fn subnormals() {
+ test_literal!(5e-324);
+ test_literal!(91e-324);
+ test_literal!(1e-322);
+ test_literal!(13245643e-320);
+ test_literal!(2.22507385851e-308);
+ test_literal!(2.1e-308);
+ test_literal!(4.9406564584124654e-324);
+}
+
+#[test]
+fn infinity() {
+ test_literal!(1e400);
+ test_literal!(1e309);
+ test_literal!(2e308);
+ test_literal!(1.7976931348624e308);
+}
+
+#[test]
+fn zero() {
+ test_literal!(0.0);
+ test_literal!(1e-325);
+ test_literal!(1e-326);
+ test_literal!(1e-500);
+}
+
+#[test]
+fn fast_path_correct() {
+ // This number triggers the fast path and is handled incorrectly when compiling on
+ // x86 without SSE2 (i.e., using the x87 FPU stack).
+ test_literal!(1.448997445238699);
+}
+
+#[test]
+fn lonely_dot() {
+ assert!(".".parse::<f32>().is_err());
+ assert!(".".parse::<f64>().is_err());
+}
+
+#[test]
+fn exponentiated_dot() {
+ assert!(".e0".parse::<f32>().is_err());
+ assert!(".e0".parse::<f64>().is_err());
+}
+
+#[test]
+fn lonely_sign() {
+ assert!("+".parse::<f32>().is_err());
+ assert!("-".parse::<f64>().is_err());
+}
+
+#[test]
+fn whitespace() {
+ assert!(" 1.0".parse::<f32>().is_err());
+ assert!("1.0 ".parse::<f64>().is_err());
+}
+
+#[test]
+fn nan() {
+ assert!("NaN".parse::<f32>().unwrap().is_nan());
+ assert!("NaN".parse::<f64>().unwrap().is_nan());
+}
+
+#[test]
+fn inf() {
+ assert_eq!("inf".parse(), Ok(f64::INFINITY));
+ assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
+ assert_eq!("inf".parse(), Ok(f32::INFINITY));
+ assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));
+}
+
+#[test]
+fn massive_exponent() {
+ let max = i64::MAX;
+ assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
+ assert_eq!(format!("1e-{max}000").parse(), Ok(0.0));
+ assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
+}
+
+#[test]
+fn borderline_overflow() {
+ let mut s = "0.".to_string();
+ for _ in 0..375 {
+ s.push('3');
+ }
+ // At the time of this writing, this returns Err(..), but this is a bug that should be fixed.
+ // It makes no sense to enshrine that in a test, the important part is that it doesn't panic.
+ let _ = s.parse::<f64>();
+}
diff --git a/library/core/tests/num/dec2flt/parse.rs b/library/core/tests/num/dec2flt/parse.rs
new file mode 100644
index 000000000..edc77377d
--- /dev/null
+++ b/library/core/tests/num/dec2flt/parse.rs
@@ -0,0 +1,177 @@
+use core::num::dec2flt::number::Number;
+use core::num::dec2flt::parse::parse_number;
+use core::num::dec2flt::{dec2flt, pfe_invalid};
+
+fn new_number(e: i64, m: u64) -> Number {
+ Number { exponent: e, mantissa: m, negative: false, many_digits: false }
+}
+
+#[test]
+fn missing_pieces() {
+ let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"];
+ for &s in permutations {
+ assert_eq!(dec2flt::<f64>(s), Err(pfe_invalid()));
+ }
+}
+
+#[test]
+fn invalid_chars() {
+ let invalid = "r,?<j";
+ let error = Err(pfe_invalid());
+ let valid_strings = &["123", "666.", ".1", "5e1", "7e-3", "0.0e+1"];
+ for c in invalid.chars() {
+ for s in valid_strings {
+ for i in 0..s.len() {
+ let mut input = String::new();
+ input.push_str(s);
+ input.insert(i, c);
+ assert!(dec2flt::<f64>(&input) == error, "did not reject invalid {:?}", input);
+ }
+ }
+ }
+}
+
+fn parse_positive(s: &[u8]) -> Option<Number> {
+ parse_number(s, false)
+}
+
+#[test]
+fn valid() {
+ assert_eq!(parse_positive(b"123.456e789"), Some(new_number(786, 123456)));
+ assert_eq!(parse_positive(b"123.456e+789"), Some(new_number(786, 123456)));
+ assert_eq!(parse_positive(b"123.456e-789"), Some(new_number(-792, 123456)));
+ assert_eq!(parse_positive(b".050"), Some(new_number(-3, 50)));
+ assert_eq!(parse_positive(b"999"), Some(new_number(0, 999)));
+ assert_eq!(parse_positive(b"1.e300"), Some(new_number(300, 1)));
+ assert_eq!(parse_positive(b".1e300"), Some(new_number(299, 1)));
+ assert_eq!(parse_positive(b"101e-33"), Some(new_number(-33, 101)));
+ let zeros = "0".repeat(25);
+ let s = format!("1.5e{zeros}");
+ assert_eq!(parse_positive(s.as_bytes()), Some(new_number(-1, 15)));
+}
+
+macro_rules! assert_float_result_bits_eq {
+ ($bits:literal, $ty:ty, $str:literal) => {{
+ let p = dec2flt::<$ty>($str);
+ assert_eq!(p.map(|x| x.to_bits()), Ok($bits));
+ }};
+}
+
+#[test]
+fn issue31109() {
+ // Regression test for #31109.
+ // Ensure the test produces a valid float with the expected bit pattern.
+ assert_float_result_bits_eq!(
+ 0x3fd5555555555555,
+ f64,
+ "0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"
+ );
+}
+
+#[test]
+fn issue31407() {
+ // Regression test for #31407.
+ // Ensure the test produces a valid float with the expected bit pattern.
+ assert_float_result_bits_eq!(
+ 0x1752a64e34ba0d3,
+ f64,
+ "1234567890123456789012345678901234567890e-340"
+ );
+ assert_float_result_bits_eq!(
+ 0xfffffffffffff,
+ f64,
+ "2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308"
+ );
+ assert_float_result_bits_eq!(
+ 0x10000000000000,
+ f64,
+ "2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875e-308"
+ );
+ assert_float_result_bits_eq!(
+ 0x10000000000000,
+ f64,
+ "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000222507385850720138309023271733240406421921598046233183055332741688720443481391819585428315901251102056406733973103581100515243416155346010885601238537771882113077799353200233047961014744258363607192156504694250373420837525080665061665815894872049117996859163964850063590877011830487479978088775374994945158045160505091539985658247081864511353793580499211598108576605199243335211435239014879569960959128889160299264151106346631339366347758651302937176204732563178148566435087212282863764204484681140761391147706280168985324411002416144742161856716615054015428508471675290190316132277889672970737312333408698898317506783884692609277397797285865965494109136909540613646756870239867831529068098461721092462539672851562500000000000000001"
+ );
+ assert_float_result_bits_eq!(
+ 0x7fefffffffffffff,
+ f64,
+ "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.9999999999999999999999999999999999999999999999999999999999999999999999"
+ );
+ assert_float_result_bits_eq!(0x0, f64, "2.47032822920623272e-324");
+ assert_float_result_bits_eq!(
+ 0x8000000,
+ f64,
+ "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316"
+ );
+ assert_float_result_bits_eq!(
+ 0x10000,
+ f64,
+ "3.237883913302901289588352412501532174863037669423108059901297049552301970670676565786835742587799557860615776559838283435514391084153169252689190564396459577394618038928365305143463955100356696665629202017331344031730044369360205258345803431471660032699580731300954848363975548690010751530018881758184174569652173110473696022749934638425380623369774736560008997404060967498028389191878963968575439222206416981462690113342524002724385941651051293552601421155333430225237291523843322331326138431477823591142408800030775170625915670728657003151953664260769822494937951845801530895238439819708403389937873241463484205608000027270531106827387907791444918534771598750162812548862768493201518991668028251730299953143924168545708663913273994694463908672332763671875E-319"
+ );
+ assert_float_result_bits_eq!(
+ 0x800000000100,
+ f64,
+ "6.953355807847677105972805215521891690222119817145950754416205607980030131549636688806115726399441880065386399864028691275539539414652831584795668560082999889551357784961446896042113198284213107935110217162654939802416034676213829409720583759540476786936413816541621287843248433202369209916612249676005573022703244799714622116542188837770376022371172079559125853382801396219552418839469770514904192657627060319372847562301074140442660237844114174497210955449896389180395827191602886654488182452409583981389442783377001505462015745017848754574668342161759496661766020028752888783387074850773192997102997936619876226688096314989645766000479009083731736585750335262099860150896718774401964796827166283225641992040747894382698751809812609536720628966577351093292236328125E-310"
+ );
+ assert_float_result_bits_eq!(
+ 0x10800,
+ f64,
+ "3.339068557571188581835713701280943911923401916998521771655656997328440314559615318168849149074662609099998113009465566426808170378434065722991659642619467706034884424989741080790766778456332168200464651593995817371782125010668346652995912233993254584461125868481633343674905074271064409763090708017856584019776878812425312008812326260363035474811532236853359905334625575404216060622858633280744301892470300555678734689978476870369853549413277156622170245846166991655321535529623870646888786637528995592800436177901746286272273374471701452991433047257863864601424252024791567368195056077320885329384322332391564645264143400798619665040608077549162173963649264049738362290606875883456826586710961041737908872035803481241600376705491726170293986797332763671875E-319"
+ );
+ assert_float_result_bits_eq!(
+ 0x0,
+ f64,
+ "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x0,
+ f64,
+ "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x1,
+ f64,
+ "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x1,
+ f64,
+ "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x2,
+ f64,
+ "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x2,
+ f64,
+ "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001e-324"
+ );
+ assert_float_result_bits_eq!(
+ 0x6c9a143590c14,
+ f64,
+ "94393431193180696942841837085033647913224148539854e-358"
+ );
+ assert_float_result_bits_eq!(
+ 0x7802665fd9600,
+ f64,
+ "104308485241983990666713401708072175773165034278685682646111762292409330928739751702404658197872319129036519947435319418387839758990478549477777586673075945844895981012024387992135617064532141489278815239849108105951619997829153633535314849999674266169258928940692239684771590065027025835804863585454872499320500023126142553932654370362024104462255244034053203998964360882487378334860197725139151265590832887433736189468858614521708567646743455601905935595381852723723645799866672558576993978025033590728687206296379801363024094048327273913079612469982585674824156000783167963081616214710691759864332339239688734656548790656486646106983450809073750535624894296242072010195710276073042036425579852459556183541199012652571123898996574563824424330960027873516082763671875e-1075"
+ );
+}
+
+#[test]
+fn many_digits() {
+ // Check large numbers of digits to ensure we have cases where significant
+ // digits (above Decimal::MAX_DIGITS) occurs.
+ assert_float_result_bits_eq!(
+ 0x7ffffe,
+ f32,
+ "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38"
+ );
+ assert_float_result_bits_eq!(
+ 0x7ffffe,
+ f32,
+ "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38"
+ );
+}
diff --git a/library/core/tests/num/flt2dec/estimator.rs b/library/core/tests/num/flt2dec/estimator.rs
new file mode 100644
index 000000000..da203b5f3
--- /dev/null
+++ b/library/core/tests/num/flt2dec/estimator.rs
@@ -0,0 +1,62 @@
+use core::num::flt2dec::estimator::*;
+
+#[test]
+fn test_estimate_scaling_factor() {
+ macro_rules! assert_almost_eq {
+ ($actual:expr, $expected:expr) => {{
+ let actual = $actual;
+ let expected = $expected;
+ println!(
+ "{} - {} = {} - {} = {}",
+ stringify!($expected),
+ stringify!($actual),
+ expected,
+ actual,
+ expected - actual
+ );
+ assert!(
+ expected == actual || expected == actual + 1,
+ "expected {}, actual {}",
+ expected,
+ actual
+ );
+ }};
+ }
+
+ assert_almost_eq!(estimate_scaling_factor(1, 0), 0);
+ assert_almost_eq!(estimate_scaling_factor(2, 0), 1);
+ assert_almost_eq!(estimate_scaling_factor(10, 0), 1);
+ assert_almost_eq!(estimate_scaling_factor(11, 0), 2);
+ assert_almost_eq!(estimate_scaling_factor(100, 0), 2);
+ assert_almost_eq!(estimate_scaling_factor(101, 0), 3);
+ assert_almost_eq!(estimate_scaling_factor(10000000000000000000, 0), 19);
+ assert_almost_eq!(estimate_scaling_factor(10000000000000000001, 0), 20);
+
+ // 1/2^20 = 0.00000095367...
+ assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000, -20), -6);
+ assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000 + 1, -20), -5);
+ assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000, -20), -5);
+ assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000 + 1, -20), -4);
+ assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000, -20), -4);
+ assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000 + 1, -20), -3);
+ assert_almost_eq!(estimate_scaling_factor(1048575, -20), 0);
+ assert_almost_eq!(estimate_scaling_factor(1048576, -20), 0);
+ assert_almost_eq!(estimate_scaling_factor(1048577, -20), 1);
+ assert_almost_eq!(estimate_scaling_factor(10485759999999999999, -20), 13);
+ assert_almost_eq!(estimate_scaling_factor(10485760000000000000, -20), 13);
+ assert_almost_eq!(estimate_scaling_factor(10485760000000000001, -20), 14);
+
+ // extreme values:
+ // 2^-1074 = 4.94065... * 10^-324
+ // (2^53-1) * 2^971 = 1.79763... * 10^308
+ assert_almost_eq!(estimate_scaling_factor(1, -1074), -323);
+ assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309);
+
+ // Miri is too slow
+ let step = if cfg!(miri) { 37 } else { 1 };
+
+ for i in (-1074..972).step_by(step) {
+ let expected = super::ldexp_f64(1.0, i).log10().ceil();
+ assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16);
+ }
+}
diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs
new file mode 100644
index 000000000..798473bbd
--- /dev/null
+++ b/library/core/tests/num/flt2dec/mod.rs
@@ -0,0 +1,1172 @@
+use std::mem::MaybeUninit;
+use std::{fmt, str};
+
+use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded};
+use core::num::flt2dec::{round_up, Sign, MAX_SIG_DIGITS};
+use core::num::flt2dec::{
+ to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str,
+};
+use core::num::fmt::{Formatted, Part};
+
+pub use test::Bencher;
+
+mod estimator;
+mod strategy {
+ mod dragon;
+ mod grisu;
+}
+mod random;
+
+pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
+ match decode(v).1 {
+ FullDecoded::Finite(decoded) => decoded,
+ full_decoded => panic!("expected finite, got {full_decoded:?} instead"),
+ }
+}
+
+macro_rules! check_shortest {
+ ($f:ident($v:expr) => $buf:expr, $exp:expr) => (
+ check_shortest!($f($v) => $buf, $exp;
+ "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}",
+ v = stringify!($v))
+ );
+
+ ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => (
+ check_shortest!($f{$($k: $v),+} => $buf, $exp;
+ "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}",
+ v = Decoded { $($k: $v),+ })
+ );
+
+ ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({
+ let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS];
+ let (buf, k) = $f(&decode_finite($v), &mut buf);
+ assert!((buf, k) == ($buf, $exp),
+ $fmt, actual = (str::from_utf8(buf).unwrap(), k),
+ expected = (str::from_utf8($buf).unwrap(), $exp),
+ $($key = $val),*);
+ });
+
+ ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr;
+ $fmt:expr, $($key:ident = $val:expr),*) => ({
+ let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS];
+ let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf);
+ assert!((buf, k) == ($buf, $exp),
+ $fmt, actual = (str::from_utf8(buf).unwrap(), k),
+ expected = (str::from_utf8($buf).unwrap(), $exp),
+ $($key = $val),*);
+ })
+}
+
+macro_rules! try_exact {
+ ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr;
+ $fmt:expr, $($key:ident = $val:expr),*) => ({
+ let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN);
+ assert!((buf, k) == ($expected, $expectedk),
+ $fmt, actual = (str::from_utf8(buf).unwrap(), k),
+ expected = (str::from_utf8($expected).unwrap(), $expectedk),
+ $($key = $val),*);
+ })
+}
+
+macro_rules! try_fixed {
+ ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr;
+ $fmt:expr, $($key:ident = $val:expr),*) => ({
+ let (buf, k) = $f($decoded, &mut $buf[..], $request);
+ assert!((buf, k) == ($expected, $expectedk),
+ $fmt, actual = (str::from_utf8(buf).unwrap(), k),
+ expected = (str::from_utf8($expected).unwrap(), $expectedk),
+ $($key = $val),*);
+ })
+}
+
+fn ldexp_f32(a: f32, b: i32) -> f32 {
+ ldexp_f64(a as f64, b) as f32
+}
+
+fn ldexp_f64(a: f64, b: i32) -> f64 {
+ extern "C" {
+ fn ldexp(x: f64, n: i32) -> f64;
+ }
+ // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly
+ // cause undefined behavior
+ unsafe { ldexp(a, b) }
+}
+
+fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16)
+where
+ T: DecodableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ // use a large enough buffer
+ let mut buf = [MaybeUninit::new(b'_'); 1024];
+ let mut expected_ = [b'_'; 1024];
+
+ let decoded = decode_finite(v);
+ let cut = expected.iter().position(|&c| c == b' ');
+
+ // check significant digits
+ for i in 1..cut.unwrap_or(expected.len() - 1) {
+ expected_[..i].copy_from_slice(&expected[..i]);
+ let mut expectedk_ = expectedk;
+ if expected[i] >= b'5' {
+ // check if this is a rounding-to-even case.
+ // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even.
+ if !(i + 1 < expected.len()
+ && expected[i - 1] & 1 == 0
+ && expected[i] == b'5'
+ && expected[i + 1] == b' ')
+ {
+ // if this returns true, expected_[..i] is all `9`s and being rounded up.
+ // we should always return `100..00` (`i` digits) instead, since that's
+ // what we can came up with `i` digits anyway. `round_up` assumes that
+ // the adjustment to the length is done by caller, which we simply ignore.
+ if let Some(_) = round_up(&mut expected_[..i]) {
+ expectedk_ += 1;
+ }
+ }
+ }
+
+ try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_;
+ "exact sigdigit mismatch for v={v}, i={i}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr, i = i);
+ try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_;
+ "fixed sigdigit mismatch for v={v}, i={i}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr, i = i);
+ }
+
+ // check exact rounding for zero- and negative-width cases
+ let start;
+ if expected[0] >= b'5' {
+ try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1;
+ "zero-width rounding-up mismatch for v={v}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr);
+ start = 1;
+ } else {
+ start = 0;
+ }
+ for i in start..-10 {
+ try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk;
+ "rounding-down mismatch for v={v}, i={i}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr, i = -i);
+ }
+
+ // check infinite zero digits
+ if let Some(cut) = cut {
+ for i in cut..expected.len() - 1 {
+ expected_[..cut].copy_from_slice(&expected[..cut]);
+ for c in &mut expected_[cut..i] {
+ *c = b'0';
+ }
+
+ try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk;
+ "exact infzero mismatch for v={v}, i={i}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr, i = i);
+ try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk;
+ "fixed infzero mismatch for v={v}, i={i}: \
+ actual {actual:?}, expected {expected:?}",
+ v = vstr, i = i);
+ }
+ }
+}
+
+trait TestableFloat: DecodableFloat + fmt::Display {
+ /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`.
+ /// This is used for testing.
+ fn ldexpi(f: i64, exp: isize) -> Self;
+}
+
+impl TestableFloat for f32 {
+ fn ldexpi(f: i64, exp: isize) -> Self {
+ f as Self * (exp as Self).exp2()
+ }
+}
+
+impl TestableFloat for f64 {
+ fn ldexpi(f: i64, exp: isize) -> Self {
+ f as Self * (exp as Self).exp2()
+ }
+}
+
+fn check_exact_one<F, T>(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16)
+where
+ T: TestableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ // use a large enough buffer
+ let mut buf = [MaybeUninit::new(b'_'); 1024];
+ let v: T = TestableFloat::ldexpi(x, e);
+ let decoded = decode_finite(v);
+
+ try_exact!(f(&decoded) => &mut buf, &expected, expectedk;
+ "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}",
+ x = x, e = e, t = tstr);
+ try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk;
+ "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}",
+ x = x, e = e, t = tstr);
+}
+
+macro_rules! check_exact {
+ ($f:ident($v:expr) => $buf:expr, $exp:expr) => {
+ check_exact(|d, b, k| $f(d, b, k), $v, stringify!($v), $buf, $exp)
+ };
+}
+
+macro_rules! check_exact_one {
+ ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => {
+ check_exact_one::<_, $t>(|d, b, k| $f(d, b, k), $x, $e, stringify!($t), $buf, $exp)
+ };
+}
+
+// in the following comments, three numbers are spaced by 1 ulp apart,
+// and the second one is being formatted.
+//
+// some tests are derived from [1].
+//
+// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion
+// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z
+
+pub fn f32_shortest_sanity_test<F>(mut f: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ // 0.0999999940395355224609375
+ // 0.100000001490116119384765625
+ // 0.10000000894069671630859375
+ check_shortest!(f(0.1f32) => b"1", 0);
+
+ // 0.333333313465118408203125
+ // 0.3333333432674407958984375 (1/3 in the default rounding)
+ // 0.33333337306976318359375
+ check_shortest!(f(1.0f32/3.0) => b"33333334", 0);
+
+ // 10^1 * 0.31415917873382568359375
+ // 10^1 * 0.31415920257568359375
+ // 10^1 * 0.31415922641754150390625
+ check_shortest!(f(3.141592f32) => b"3141592", 1);
+
+ // 10^18 * 0.31415916243714048
+ // 10^18 * 0.314159196796878848
+ // 10^18 * 0.314159231156617216
+ check_shortest!(f(3.141592e17f32) => b"3141592", 18);
+
+ // regression test for decoders
+ // 10^8 * 0.3355443
+ // 10^8 * 0.33554432
+ // 10^8 * 0.33554436
+ check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8);
+
+ // 10^39 * 0.340282326356119256160033759537265639424
+ // 10^39 * 0.34028234663852885981170418348451692544
+ // 10^39 * 0.340282366920938463463374607431768211456
+ check_shortest!(f(f32::MAX) => b"34028235", 39);
+
+ // 10^-37 * 0.1175494210692441075487029444849287348827...
+ // 10^-37 * 0.1175494350822287507968736537222245677818...
+ // 10^-37 * 0.1175494490952133940450443629595204006810...
+ check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37);
+
+ // 10^-44 * 0
+ // 10^-44 * 0.1401298464324817070923729583289916131280...
+ // 10^-44 * 0.2802596928649634141847459166579832262560...
+ let minf32 = ldexp_f32(1.0, -149);
+ check_shortest!(f(minf32) => b"1", -44);
+}
+
+pub fn f32_exact_sanity_test<F>(mut f: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ let minf32 = ldexp_f32(1.0, -149);
+
+ check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0);
+ check_exact!(f(0.5f32) => b"5 ", 0);
+ check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0);
+ check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1);
+ check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18);
+ check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39);
+ check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37);
+ check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44);
+
+ // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP
+ check_exact_one!(f(12676506, -102; f32) => b"2", -23);
+ check_exact_one!(f(12676506, -103; f32) => b"12", -23);
+ check_exact_one!(f(15445013, 86; f32) => b"119", 34);
+ check_exact_one!(f(13734123, -138; f32) => b"3941", -34);
+ check_exact_one!(f(12428269, -130; f32) => b"91308", -32);
+ check_exact_one!(f(15334037, -146; f32) => b"171900", -36);
+ check_exact_one!(f(11518287, -41; f32) => b"5237910", -5);
+ check_exact_one!(f(12584953, -145; f32) => b"28216440", -36);
+ check_exact_one!(f(15961084, -125; f32) => b"375243281", -30);
+ check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36);
+ check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23);
+ check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11);
+
+ // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP
+ check_exact_one!(f(16093626, 69; f32) => b"1", 29);
+ check_exact_one!(f( 9983778, 25; f32) => b"34", 15);
+ check_exact_one!(f(12745034, 104; f32) => b"259", 39);
+ check_exact_one!(f(12706553, 72; f32) => b"6001", 29);
+ check_exact_one!(f(11005028, 45; f32) => b"38721", 21);
+ check_exact_one!(f(15059547, 71; f32) => b"355584", 29);
+ check_exact_one!(f(16015691, -99; f32) => b"2526831", -22);
+ check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24);
+ check_exact_one!(f(14855922, -82; f32) => b"307213267", -17);
+ check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17);
+ check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26);
+ check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36);
+}
+
+pub fn f64_shortest_sanity_test<F>(mut f: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ // 0.0999999999999999777955395074968691915273...
+ // 0.1000000000000000055511151231257827021181...
+ // 0.1000000000000000333066907387546962127089...
+ check_shortest!(f(0.1f64) => b"1", 0);
+
+ // this example is explicitly mentioned in the paper.
+ // 10^3 * 0.0999999999999999857891452847979962825775...
+ // 10^3 * 0.1 (exact)
+ // 10^3 * 0.1000000000000000142108547152020037174224...
+ check_shortest!(f(100.0f64) => b"1", 3);
+
+ // 0.3333333333333332593184650249895639717578...
+ // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding)
+ // 0.3333333333333333703407674875052180141210...
+ check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0);
+
+ // explicit test case for equally closest representations.
+ // Dragon has its own tie-breaking rule; Grisu should fall back.
+ // 10^1 * 0.1000007629394531027955395074968691915273...
+ // 10^1 * 0.100000762939453125 (exact)
+ // 10^1 * 0.1000007629394531472044604925031308084726...
+ check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1);
+
+ // 10^1 * 0.3141591999999999718085064159822650253772...
+ // 10^1 * 0.3141592000000000162174274009885266423225...
+ // 10^1 * 0.3141592000000000606263483859947882592678...
+ check_shortest!(f(3.141592f64) => b"3141592", 1);
+
+ // 10^18 * 0.314159199999999936
+ // 10^18 * 0.3141592 (exact)
+ // 10^18 * 0.314159200000000064
+ check_shortest!(f(3.141592e17f64) => b"3141592", 18);
+
+ // regression test for decoders
+ // 10^20 * 0.18446744073709549568
+ // 10^20 * 0.18446744073709551616
+ // 10^20 * 0.18446744073709555712
+ check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20);
+
+ // pathological case: high = 10^23 (exact). tie breaking should always prefer that.
+ // 10^24 * 0.099999999999999974834176
+ // 10^24 * 0.099999999999999991611392
+ // 10^24 * 0.100000000000000008388608
+ check_shortest!(f(1.0e23f64) => b"1", 24);
+
+ // 10^309 * 0.1797693134862315508561243283845062402343...
+ // 10^309 * 0.1797693134862315708145274237317043567980...
+ // 10^309 * 0.1797693134862315907729305190789024733617...
+ check_shortest!(f(f64::MAX) => b"17976931348623157", 309);
+
+ // 10^-307 * 0.2225073858507200889024586876085859887650...
+ // 10^-307 * 0.2225073858507201383090232717332404064219...
+ // 10^-307 * 0.2225073858507201877155878558578948240788...
+ check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307);
+
+ // 10^-323 * 0
+ // 10^-323 * 0.4940656458412465441765687928682213723650...
+ // 10^-323 * 0.9881312916824930883531375857364427447301...
+ let minf64 = ldexp_f64(1.0, -1074);
+ check_shortest!(f(minf64) => b"5", -323);
+}
+
+pub fn f64_exact_sanity_test<F>(mut f: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ let minf64 = ldexp_f64(1.0, -1074);
+
+ check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0);
+ check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0);
+ check_exact!(f(0.5f64) => b"5 ", 0);
+ check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0);
+ check_exact!(f(100.0f64) => b"1 ", 3);
+ check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3);
+ check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0);
+ check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1);
+ check_exact!(f(3.141592e17f64) => b"3141592 ", 18);
+ check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23);
+ check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309);
+ check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307);
+ check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\
+ 5980261432476442558568250067550727020875\
+ 1865299836361635992379796564695445717730\
+ 9266567103559397963987747960107818781263\
+ 0071319031140452784581716784898210368871\
+ 8636056998730723050006387409153564984387\
+ 3124733972731696151400317153853980741262\
+ 3856559117102665855668676818703956031062\
+ 4931945271591492455329305456544401127480\
+ 1297099995419319894090804165633245247571\
+ 4786901472678015935523861155013480352649\
+ 3472019379026810710749170333222684475333\
+ 5720832431936092382893458368060106011506\
+ 1698097530783422773183292479049825247307\
+ 7637592724787465608477820373446969953364\
+ 7017972677717585125660551199131504891101\
+ 4510378627381672509558373897335989936648\
+ 0994116420570263709027924276754456522908\
+ 7538682506419718265533447265625 ", -323);
+
+ // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP
+ check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87);
+ check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232);
+ check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88);
+ check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38);
+ check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46);
+ check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235);
+ check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121);
+ check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302);
+ check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130);
+ check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130);
+ check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44);
+ check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230);
+ check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230);
+ check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52);
+ check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285);
+ check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196);
+ check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98);
+ check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233);
+ check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233);
+ check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87);
+ check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77);
+ check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126);
+
+ // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP
+ check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303);
+ check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177);
+ check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177);
+ check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272);
+ check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27);
+ check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126);
+ check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35);
+ check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240);
+ check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218);
+ check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99);
+ check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99);
+ check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99);
+ check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59);
+ check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59);
+ check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59);
+ check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181);
+ check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66);
+ check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217);
+ check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224);
+ check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193);
+ check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98);
+ check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118);
+}
+
+pub fn more_shortest_sanity_test<F>(mut f: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1,
+ exp: 0, inclusive: true} => b"1", 18);
+ check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1,
+ exp: 0, inclusive: false} => b"99999999999999999", 17);
+}
+
+fn to_string_with_parts<F>(mut f: F) -> String
+where
+ F: for<'a> FnMut(&'a mut [MaybeUninit<u8>], &'a mut [MaybeUninit<Part<'a>>]) -> Formatted<'a>,
+{
+ let mut buf = [MaybeUninit::new(0); 1024];
+ let mut parts = [MaybeUninit::new(Part::Zero(0)); 16];
+ let formatted = f(&mut buf, &mut parts);
+ let mut ret = vec![0; formatted.len()];
+ assert_eq!(formatted.write(&mut ret), Some(ret.len()));
+ String::from_utf8(ret).unwrap()
+}
+
+pub fn to_shortest_str_test<F>(mut f_: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ use core::num::flt2dec::Sign::*;
+
+ fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String
+ where
+ T: DecodableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+ {
+ to_string_with_parts(|buf, parts| {
+ to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts)
+ })
+ }
+
+ let f = &mut f_;
+
+ assert_eq!(to_string(f, 0.0, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.0, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0");
+ assert_eq!(to_string(f, -0.0, Minus, 0), "-0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0");
+ assert_eq!(to_string(f, 0.0, Minus, 1), "0.0");
+ assert_eq!(to_string(f, 0.0, Minus, 1), "0.0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0");
+ assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000");
+
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf");
+
+ assert_eq!(to_string(f, 3.14, Minus, 0), "3.14");
+ assert_eq!(to_string(f, 3.14, Minus, 0), "3.14");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14");
+ assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14");
+ assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14");
+ assert_eq!(to_string(f, 3.14, Minus, 1), "3.14");
+ assert_eq!(to_string(f, 3.14, Minus, 2), "3.14");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400");
+ assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000");
+ assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000");
+
+ assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750");
+
+ assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000");
+ assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0");
+ assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000");
+
+ assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", ""));
+ assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", ""));
+ assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", ""));
+
+ let minf32 = ldexp_f32(1.0, -149);
+ assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", ""));
+ assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", ""));
+ assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", ""));
+
+ assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", ""));
+ assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", ""));
+ assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", ""));
+
+ let minf64 = ldexp_f64(1.0, -1074);
+ assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", ""));
+ assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", ""));
+ assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", ""));
+
+ if cfg!(miri) {
+ // Miri is too slow
+ return;
+ }
+
+ // very large output
+ assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", ""));
+}
+
+pub fn to_shortest_exp_str_test<F>(mut f_: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ use core::num::flt2dec::Sign::*;
+
+ fn to_string<T, F>(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String
+ where
+ T: DecodableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+ {
+ to_string_with_parts(|buf, parts| {
+ to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts)
+ })
+ }
+
+ let f = &mut f_;
+
+ assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0");
+ assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0");
+ assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "-0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "-0");
+ assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0");
+ assert_eq!(to_string(f, 0.0, Minus, (0, 0), false), "0e0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, (5, 9), false), "+0e0");
+ assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "-0E0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, (5, 9), false), "-0e0");
+
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), true), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), true), "+inf");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), true), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (5, 9), true), "NaN");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), true), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (5, 9), true), "-inf");
+
+ assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14");
+ assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14");
+ assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14");
+ assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14");
+ assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0");
+ assert_eq!(to_string(f, 3.14, Minus, (0, 0), false), "3.14e0");
+ assert_eq!(to_string(f, 3.14, MinusPlus, (5, 9), false), "+3.14e0");
+ assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0");
+ assert_eq!(to_string(f, -3.14, Minus, (0, 0), false), "-3.14e0");
+ assert_eq!(to_string(f, -3.14, MinusPlus, (5, 9), false), "-3.14e0");
+
+ assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1");
+ assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1");
+ assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1");
+ assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1");
+ assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1");
+ assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1");
+ assert_eq!(to_string(f, 0.1, Minus, (0, 0), false), "1e-1");
+ assert_eq!(to_string(f, 0.1, MinusPlus, (5, 9), false), "+1e-1");
+ assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1");
+ assert_eq!(to_string(f, -0.1, Minus, (0, 0), false), "-1e-1");
+ assert_eq!(to_string(f, -0.1, MinusPlus, (5, 9), false), "-1e-1");
+
+ assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11");
+ assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075");
+ assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11");
+
+ assert_eq!(to_string(f, 1.9971e20, Minus, (-4, 16), false), "1.9971e20");
+ assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000");
+ assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20");
+
+ // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here
+ assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23");
+ assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000");
+ assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23");
+
+ assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
+
+ let minf32 = ldexp_f32(1.0, -149);
+ assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45");
+ assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
+ assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
+
+ assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308");
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, (-308, 309), false),
+ format!("17976931348623157{:0>292}", "")
+ );
+ assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308");
+
+ let minf64 = ldexp_f64(1.0, -1074);
+ assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324");
+ assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
+ assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
+
+ assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1");
+}
+
+pub fn to_exact_exp_str_test<F>(mut f_: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ use core::num::flt2dec::Sign::*;
+
+ fn to_string<T, F>(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String
+ where
+ T: DecodableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+ {
+ to_string_with_parts(|buf, parts| {
+ to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts)
+ })
+ }
+
+ let f = &mut f_;
+
+ assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0");
+ assert_eq!(to_string(f, 0.0, Minus, 1, false), "0e0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 1, false), "+0e0");
+ assert_eq!(to_string(f, -0.0, Minus, 1, true), "-0E0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 1, false), "-0e0");
+ assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0");
+ assert_eq!(to_string(f, 0.0, Minus, 2, false), "0.0e0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 2, false), "+0.0e0");
+ assert_eq!(to_string(f, -0.0, Minus, 8, false), "-0.0000000e0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 8, false), "-0.0000000e0");
+
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, true), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, true), "+inf");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, true), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, true), "NaN");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, true), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, true), "-inf");
+
+ assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0");
+ assert_eq!(to_string(f, 3.14, Minus, 1, false), "3e0");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 1, false), "+3e0");
+ assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0");
+ assert_eq!(to_string(f, -3.14, Minus, 2, false), "-3.1e0");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 2, false), "-3.1e0");
+ assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0");
+ assert_eq!(to_string(f, 3.14, Minus, 3, false), "3.14e0");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 3, false), "+3.14e0");
+ assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0");
+ assert_eq!(to_string(f, -3.14, Minus, 4, false), "-3.140e0");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 4, false), "-3.140e0");
+
+ assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1");
+ assert_eq!(to_string(f, 0.195, Minus, 1, true), "2E-1");
+ assert_eq!(to_string(f, 0.195, MinusPlus, 1, true), "+2E-1");
+ assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1");
+ assert_eq!(to_string(f, -0.195, Minus, 2, true), "-2.0E-1");
+ assert_eq!(to_string(f, -0.195, MinusPlus, 2, true), "-2.0E-1");
+ assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1");
+ assert_eq!(to_string(f, 0.195, Minus, 3, true), "1.95E-1");
+ assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+1.95E-1");
+ assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1");
+ assert_eq!(to_string(f, -0.195, Minus, 4, true), "-1.950E-1");
+ assert_eq!(to_string(f, -0.195, MinusPlus, 4, true), "-1.950E-1");
+
+ assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1");
+ assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0");
+ assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0");
+ assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0");
+
+ assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25");
+ assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25");
+
+ assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7");
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 40, false),
+ "9.999999999999999547481118258862586856139e-7"
+ );
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 50, false),
+ "9.9999999999999995474811182588625868561393872369081e-7"
+ );
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 60, false),
+ "9.99999999999999954748111825886258685613938723690807819366455e-7"
+ );
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 70, false),
+ "9.999999999999999547481118258862586856139387236908078193664550781250000e-7"
+ );
+
+ assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38");
+ assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38");
+ assert_eq!(
+ to_string(f, f32::MAX, Minus, 64, false),
+ "3.402823466385288598117041834845169254400000000000000000000000000e38"
+ );
+
+ let minf32 = ldexp_f32(1.0, -149);
+ assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45");
+ assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45");
+ assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45");
+ assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45");
+ assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45");
+ assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45");
+ assert_eq!(
+ to_string(f, minf32, Minus, 64, false),
+ "1.401298464324817070923729583289916131280261941876515771757068284e-45"
+ );
+ assert_eq!(
+ to_string(f, minf32, Minus, 128, false),
+ "1.401298464324817070923729583289916131280261941876515771757068283\
+ 8897910826858606014866381883621215820312500000000000000000000000e-45"
+ );
+
+ if cfg!(miri) {
+ // Miri is too slow
+ return;
+ }
+
+ assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308");
+ assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308");
+ assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308");
+ assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308");
+ assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308");
+ assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308");
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 64, false),
+ "1.797693134862315708145274237317043567980705675258449965989174768e308"
+ );
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 128, false),
+ "1.797693134862315708145274237317043567980705675258449965989174768\
+ 0315726078002853876058955863276687817154045895351438246423432133e308"
+ );
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 256, false),
+ "1.797693134862315708145274237317043567980705675258449965989174768\
+ 0315726078002853876058955863276687817154045895351438246423432132\
+ 6889464182768467546703537516986049910576551282076245490090389328\
+ 9440758685084551339423045832369032229481658085593321233482747978e308"
+ );
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 512, false),
+ "1.797693134862315708145274237317043567980705675258449965989174768\
+ 0315726078002853876058955863276687817154045895351438246423432132\
+ 6889464182768467546703537516986049910576551282076245490090389328\
+ 9440758685084551339423045832369032229481658085593321233482747978\
+ 2620414472316873817718091929988125040402618412485836800000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000e308"
+ );
+
+ // okay, this is becoming tough. fortunately for us, this is almost the worst case.
+ let minf64 = ldexp_f64(1.0, -1074);
+ assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324");
+ assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324");
+ assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324");
+ assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324");
+ assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324");
+ assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324");
+ assert_eq!(
+ to_string(f, minf64, Minus, 64, false),
+ "4.940656458412465441765687928682213723650598026143247644255856825e-324"
+ );
+ assert_eq!(
+ to_string(f, minf64, Minus, 128, false),
+ "4.940656458412465441765687928682213723650598026143247644255856825\
+ 0067550727020875186529983636163599237979656469544571773092665671e-324"
+ );
+ assert_eq!(
+ to_string(f, minf64, Minus, 256, false),
+ "4.940656458412465441765687928682213723650598026143247644255856825\
+ 0067550727020875186529983636163599237979656469544571773092665671\
+ 0355939796398774796010781878126300713190311404527845817167848982\
+ 1036887186360569987307230500063874091535649843873124733972731696e-324"
+ );
+ assert_eq!(
+ to_string(f, minf64, Minus, 512, false),
+ "4.940656458412465441765687928682213723650598026143247644255856825\
+ 0067550727020875186529983636163599237979656469544571773092665671\
+ 0355939796398774796010781878126300713190311404527845817167848982\
+ 1036887186360569987307230500063874091535649843873124733972731696\
+ 1514003171538539807412623856559117102665855668676818703956031062\
+ 4931945271591492455329305456544401127480129709999541931989409080\
+ 4165633245247571478690147267801593552386115501348035264934720193\
+ 7902681071074917033322268447533357208324319360923828934583680601e-324"
+ );
+ assert_eq!(
+ to_string(f, minf64, Minus, 1024, false),
+ "4.940656458412465441765687928682213723650598026143247644255856825\
+ 0067550727020875186529983636163599237979656469544571773092665671\
+ 0355939796398774796010781878126300713190311404527845817167848982\
+ 1036887186360569987307230500063874091535649843873124733972731696\
+ 1514003171538539807412623856559117102665855668676818703956031062\
+ 4931945271591492455329305456544401127480129709999541931989409080\
+ 4165633245247571478690147267801593552386115501348035264934720193\
+ 7902681071074917033322268447533357208324319360923828934583680601\
+ 0601150616980975307834227731832924790498252473077637592724787465\
+ 6084778203734469699533647017972677717585125660551199131504891101\
+ 4510378627381672509558373897335989936648099411642057026370902792\
+ 4276754456522908753868250641971826553344726562500000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000e-324"
+ );
+
+ // very large output
+ assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", ""));
+ assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", ""));
+ assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", ""));
+ assert_eq!(
+ to_string(f, 1.0e-1, Minus, 80000, false),
+ format!(
+ "1.000000000000000055511151231257827021181583404541015625{:0>79945}\
+ e-1",
+ ""
+ )
+ );
+ assert_eq!(
+ to_string(f, 1.0e-20, Minus, 80000, false),
+ format!(
+ "9.999999999999999451532714542095716517295037027873924471077157760\
+ 66783064379706047475337982177734375{:0>79901}e-21",
+ ""
+ )
+ );
+}
+
+pub fn to_exact_fixed_str_test<F>(mut f_: F)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+{
+ use core::num::flt2dec::Sign::*;
+
+ fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String
+ where
+ T: DecodableFloat,
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
+ {
+ to_string_with_parts(|buf, parts| {
+ to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts)
+ })
+ }
+
+ let f = &mut f_;
+
+ assert_eq!(to_string(f, 0.0, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0");
+ assert_eq!(to_string(f, -0.0, Minus, 0), "-0");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0");
+ assert_eq!(to_string(f, 0.0, Minus, 1), "0.0");
+ assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0");
+ assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000");
+ assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000");
+
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1), "inf");
+ assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 64), "+inf");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN");
+ assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf");
+ assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf");
+
+ assert_eq!(to_string(f, 3.14, Minus, 0), "3");
+ assert_eq!(to_string(f, 3.14, Minus, 0), "3");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3");
+ assert_eq!(to_string(f, -3.14, Minus, 0), "-3");
+ assert_eq!(to_string(f, -3.14, Minus, 0), "-3");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3");
+ assert_eq!(to_string(f, 3.14, Minus, 1), "3.1");
+ assert_eq!(to_string(f, 3.14, Minus, 2), "3.14");
+ assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400");
+ assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000");
+ assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000");
+ assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000");
+
+ assert_eq!(to_string(f, 0.195, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0");
+ assert_eq!(to_string(f, -0.195, Minus, 0), "-0");
+ assert_eq!(to_string(f, -0.195, Minus, 0), "-0");
+ assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0");
+ assert_eq!(to_string(f, 0.195, Minus, 1), "0.2");
+ assert_eq!(to_string(f, 0.195, Minus, 2), "0.20");
+ assert_eq!(to_string(f, 0.195, MinusPlus, 4), "+0.1950");
+ assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500");
+ assert_eq!(to_string(f, -0.195, Minus, 6), "-0.195000");
+ assert_eq!(to_string(f, -0.195, MinusPlus, 8), "-0.19500000");
+
+ assert_eq!(to_string(f, 999.5, Minus, 0), "1000");
+ assert_eq!(to_string(f, 999.5, Minus, 1), "999.5");
+ assert_eq!(to_string(f, 999.5, Minus, 2), "999.50");
+ assert_eq!(to_string(f, 999.5, Minus, 3), "999.500");
+ assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000");
+
+ assert_eq!(to_string(f, 0.5, Minus, 0), "1");
+ assert_eq!(to_string(f, 0.5, Minus, 1), "0.5");
+ assert_eq!(to_string(f, 0.5, Minus, 2), "0.50");
+ assert_eq!(to_string(f, 0.5, Minus, 3), "0.500");
+
+ assert_eq!(to_string(f, 0.95, Minus, 0), "1");
+ assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95
+ assert_eq!(to_string(f, 0.95, Minus, 2), "0.95");
+ assert_eq!(to_string(f, 0.95, Minus, 3), "0.950");
+ assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000");
+ assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994");
+
+ assert_eq!(to_string(f, 0.095, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.095, Minus, 1), "0.1");
+ assert_eq!(to_string(f, 0.095, Minus, 2), "0.10");
+ assert_eq!(to_string(f, 0.095, Minus, 3), "0.095");
+ assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950");
+ assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000");
+ assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625");
+
+ assert_eq!(to_string(f, 0.0095, Minus, 0), "0");
+ assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0");
+ assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01");
+ assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095
+ assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095");
+ assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950");
+ assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000");
+ assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267");
+
+ assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto
+ assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000");
+ assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501");
+
+ assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664");
+ assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0");
+ assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000");
+
+ assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589");
+ assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614");
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 55),
+ "0.0000009999999999999999547481118258862586856139387236908"
+ );
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 65),
+ "0.00000099999999999999995474811182588625868561393872369080781936646"
+ );
+ assert_eq!(
+ to_string(f, 1.0e-6, Minus, 75),
+ "0.000000999999999999999954748111825886258685613938723690807819366455078125000"
+ );
+
+ assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440");
+ assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0");
+ assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00");
+
+ if cfg!(miri) {
+ // Miri is too slow
+ return;
+ }
+
+ let minf32 = ldexp_f32(1.0, -149);
+ assert_eq!(to_string(f, minf32, Minus, 0), "0");
+ assert_eq!(to_string(f, minf32, Minus, 1), "0.0");
+ assert_eq!(to_string(f, minf32, Minus, 2), "0.00");
+ assert_eq!(to_string(f, minf32, Minus, 4), "0.0000");
+ assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000");
+ assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000");
+ assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000");
+ assert_eq!(
+ to_string(f, minf32, Minus, 64),
+ "0.0000000000000000000000000000000000000000000014012984643248170709"
+ );
+ assert_eq!(
+ to_string(f, minf32, Minus, 128),
+ "0.0000000000000000000000000000000000000000000014012984643248170709\
+ 2372958328991613128026194187651577175706828388979108268586060149"
+ );
+ assert_eq!(
+ to_string(f, minf32, Minus, 256),
+ "0.0000000000000000000000000000000000000000000014012984643248170709\
+ 2372958328991613128026194187651577175706828388979108268586060148\
+ 6638188362121582031250000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000"
+ );
+
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 0),
+ "1797693134862315708145274237317043567980705675258449965989174768\
+ 0315726078002853876058955863276687817154045895351438246423432132\
+ 6889464182768467546703537516986049910576551282076245490090389328\
+ 9440758685084551339423045832369032229481658085593321233482747978\
+ 26204144723168738177180919299881250404026184124858368"
+ );
+ assert_eq!(
+ to_string(f, f64::MAX, Minus, 10),
+ "1797693134862315708145274237317043567980705675258449965989174768\
+ 0315726078002853876058955863276687817154045895351438246423432132\
+ 6889464182768467546703537516986049910576551282076245490090389328\
+ 9440758685084551339423045832369032229481658085593321233482747978\
+ 26204144723168738177180919299881250404026184124858368.0000000000"
+ );
+
+ let minf64 = ldexp_f64(1.0, -1074);
+ assert_eq!(to_string(f, minf64, Minus, 0), "0");
+ assert_eq!(to_string(f, minf64, Minus, 1), "0.0");
+ assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000");
+ assert_eq!(
+ to_string(f, minf64, Minus, 100),
+ "0.0000000000000000000000000000000000000000000000000000000000000000\
+ 000000000000000000000000000000000000"
+ );
+ assert_eq!(
+ to_string(f, minf64, Minus, 1000),
+ "0.0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0000000000000000000000000000000000000000000000000000000000000000\
+ 0004940656458412465441765687928682213723650598026143247644255856\
+ 8250067550727020875186529983636163599237979656469544571773092665\
+ 6710355939796398774796010781878126300713190311404527845817167848\
+ 9821036887186360569987307230500063874091535649843873124733972731\
+ 6961514003171538539807412623856559117102665855668676818703956031\
+ 0624931945271591492455329305456544401127480129709999541931989409\
+ 0804165633245247571478690147267801593552386115501348035264934720\
+ 1937902681071074917033322268447533357208324319360923828934583680\
+ 6010601150616980975307834227731832924790498252473077637592724787\
+ 4656084778203734469699533647017972677717585125660551199131504891\
+ 1014510378627381672509558373897335989937"
+ );
+
+ // very large output
+ assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", ""));
+ assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", ""));
+ assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", ""));
+ assert_eq!(
+ to_string(f, 1.0e-1, Minus, 80000),
+ format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "")
+ );
+ assert_eq!(
+ to_string(f, 1.0e-20, Minus, 80000),
+ format!(
+ "0.0000000000000000000099999999999999994515327145420957165172950370\
+ 2787392447107715776066783064379706047475337982177734375{:0>79881}",
+ ""
+ )
+ );
+}
diff --git a/library/core/tests/num/flt2dec/random.rs b/library/core/tests/num/flt2dec/random.rs
new file mode 100644
index 000000000..d09500393
--- /dev/null
+++ b/library/core/tests/num/flt2dec/random.rs
@@ -0,0 +1,202 @@
+#![cfg(not(target_arch = "wasm32"))]
+
+use std::mem::MaybeUninit;
+use std::str;
+
+use core::num::flt2dec::strategy::grisu::format_exact_opt;
+use core::num::flt2dec::strategy::grisu::format_shortest_opt;
+use core::num::flt2dec::MAX_SIG_DIGITS;
+use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded};
+
+use rand::distributions::{Distribution, Uniform};
+use rand::rngs::StdRng;
+use rand::SeedableRng;
+
+pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
+ match decode(v).1 {
+ FullDecoded::Finite(decoded) => decoded,
+ full_decoded => panic!("expected finite, got {full_decoded:?} instead"),
+ }
+}
+
+fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+ G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+ V: FnMut(usize) -> Decoded,
+{
+ assert!(k <= 1024);
+
+ let mut npassed = 0; // f(x) = Some(g(x))
+ let mut nignored = 0; // f(x) = None
+
+ for i in 0..n {
+ if (i & 0xfffff) == 0 {
+ println!(
+ "in progress, {:x}/{:x} (ignored={} passed={} failed={})",
+ i,
+ n,
+ nignored,
+ npassed,
+ i - nignored - npassed
+ );
+ }
+
+ let decoded = v(i);
+ let mut buf1 = [MaybeUninit::new(0); 1024];
+ if let Some((buf1, e1)) = f(&decoded, &mut buf1[..k]) {
+ let mut buf2 = [MaybeUninit::new(0); 1024];
+ let (buf2, e2) = g(&decoded, &mut buf2[..k]);
+ if e1 == e2 && buf1 == buf2 {
+ npassed += 1;
+ } else {
+ println!(
+ "equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
+ i,
+ n,
+ decoded,
+ str::from_utf8(buf1).unwrap(),
+ e1,
+ str::from_utf8(buf2).unwrap(),
+ e2
+ );
+ }
+ } else {
+ nignored += 1;
+ }
+ }
+ println!(
+ "{}({}): done, ignored={} passed={} failed={}",
+ func,
+ k,
+ nignored,
+ npassed,
+ n - nignored - npassed
+ );
+ assert!(
+ nignored + npassed == n,
+ "{}({}): {} out of {} values returns an incorrect value!",
+ func,
+ k,
+ n - nignored - npassed,
+ n
+ );
+ (npassed, nignored)
+}
+
+pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+ G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ if cfg!(target_os = "emscripten") {
+ return; // using rng pulls in i128 support, which doesn't work
+ }
+ let mut rng = StdRng::from_entropy();
+ let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000);
+ iterate("f32_random_equivalence_test", k, n, f, g, |_| {
+ let x = f32::from_bits(f32_range.sample(&mut rng));
+ decode_finite(x)
+ });
+}
+
+pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+ G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ if cfg!(target_os = "emscripten") {
+ return; // using rng pulls in i128 support, which doesn't work
+ }
+ let mut rng = StdRng::from_entropy();
+ let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
+ iterate("f64_random_equivalence_test", k, n, f, g, |_| {
+ let x = f64::from_bits(f64_range.sample(&mut rng));
+ decode_finite(x)
+ });
+}
+
+pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
+where
+ F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
+ G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
+{
+ // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
+ // so why not simply testing all of them?
+ //
+ // this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
+ // but with `-C opt-level=3 -C lto` this only takes about an hour or so.
+
+ // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e., all finite ranges
+ let (npassed, nignored) =
+ iterate("f32_exhaustive_equivalence_test", k, 0x7f7f_ffff, f, g, |i: usize| {
+ let x = f32::from_bits(i as u32 + 1);
+ decode_finite(x)
+ });
+ assert_eq!((npassed, nignored), (2121451881, 17643158));
+}
+
+#[test]
+fn shortest_random_equivalence_test() {
+ use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+ // Miri is too slow
+ let n = if cfg!(miri) { 10 } else { 10_000 };
+
+ f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
+ f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
+}
+
+#[test]
+#[ignore] // it is too expensive
+fn shortest_f32_exhaustive_equivalence_test() {
+ // it is hard to directly test the optimality of the output, but we can at least test if
+ // two different algorithms agree to each other.
+ //
+ // this reports the progress and the number of f32 values returned `None`.
+ // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
+ // `done, ignored=17643158 passed=2121451881 failed=0`.
+
+ use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+ f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
+}
+
+#[test]
+#[ignore] // it is too expensive
+fn shortest_f64_hard_random_equivalence_test() {
+ // this again probably has to use appropriate rustc flags.
+
+ use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+ f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000);
+}
+
+#[test]
+fn exact_f32_random_equivalence_test() {
+ use core::num::flt2dec::strategy::dragon::format_exact as fallback;
+ // Miri is too slow
+ let n = if cfg!(miri) { 3 } else { 1_000 };
+
+ for k in 1..21 {
+ f32_random_equivalence_test(
+ |d, buf| format_exact_opt(d, buf, i16::MIN),
+ |d, buf| fallback(d, buf, i16::MIN),
+ k,
+ n,
+ );
+ }
+}
+
+#[test]
+fn exact_f64_random_equivalence_test() {
+ use core::num::flt2dec::strategy::dragon::format_exact as fallback;
+ // Miri is too slow
+ let n = if cfg!(miri) { 2 } else { 1_000 };
+
+ for k in 1..21 {
+ f64_random_equivalence_test(
+ |d, buf| format_exact_opt(d, buf, i16::MIN),
+ |d, buf| fallback(d, buf, i16::MIN),
+ k,
+ n,
+ );
+ }
+}
diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/core/tests/num/flt2dec/strategy/dragon.rs
new file mode 100644
index 000000000..fc2e724a2
--- /dev/null
+++ b/library/core/tests/num/flt2dec/strategy/dragon.rs
@@ -0,0 +1,63 @@
+use super::super::*;
+use core::num::bignum::Big32x40 as Big;
+use core::num::flt2dec::strategy::dragon::*;
+
+#[test]
+fn test_mul_pow10() {
+ let mut prevpow10 = Big::from_small(1);
+ for i in 1..340 {
+ let mut curpow10 = Big::from_small(1);
+ mul_pow10(&mut curpow10, i);
+ assert_eq!(curpow10, *prevpow10.clone().mul_small(10));
+ prevpow10 = curpow10;
+ }
+}
+
+#[test]
+fn shortest_sanity_test() {
+ f64_shortest_sanity_test(format_shortest);
+ f32_shortest_sanity_test(format_shortest);
+ more_shortest_sanity_test(format_shortest);
+}
+
+#[test]
+#[cfg_attr(miri, ignore)] // Miri is too slow
+fn exact_sanity_test() {
+ // This test ends up running what I can only assume is some corner-ish case
+ // of the `exp2` library function, defined in whatever C runtime we're
+ // using. In VS 2013 this function apparently had a bug as this test fails
+ // when linked, but with VS 2015 the bug appears fixed as the test runs just
+ // fine.
+ //
+ // The bug seems to be a difference in return value of `exp2(-1057)`, where
+ // in VS 2013 it returns a double with the bit pattern 0x2 and in VS 2015 it
+ // returns 0x20000.
+ //
+ // For now just ignore this test entirely on MSVC as it's tested elsewhere
+ // anyway and we're not super interested in testing each platform's exp2
+ // implementation.
+ if !cfg!(target_env = "msvc") {
+ f64_exact_sanity_test(format_exact);
+ }
+ f32_exact_sanity_test(format_exact);
+}
+
+#[test]
+fn test_to_shortest_str() {
+ to_shortest_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_shortest_exp_str() {
+ to_shortest_exp_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_exact_exp_str() {
+ to_exact_exp_str_test(format_exact);
+}
+
+#[test]
+fn test_to_exact_fixed_str() {
+ to_exact_fixed_str_test(format_exact);
+}
diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/core/tests/num/flt2dec/strategy/grisu.rs
new file mode 100644
index 000000000..b59a3b9b7
--- /dev/null
+++ b/library/core/tests/num/flt2dec/strategy/grisu.rs
@@ -0,0 +1,72 @@
+use super::super::*;
+use core::num::flt2dec::strategy::grisu::*;
+
+#[test]
+#[cfg_attr(miri, ignore)] // Miri is too slow
+fn test_cached_power() {
+ assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E);
+ assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E);
+
+ for e in -1137..961 {
+ // full range for f64
+ let low = ALPHA - e - 64;
+ let high = GAMMA - e - 64;
+ let (_k, cached) = cached_power(low, high);
+ assert!(
+ low <= cached.e && cached.e <= high,
+ "cached_power({}, {}) = {:?} is incorrect",
+ low,
+ high,
+ cached
+ );
+ }
+}
+
+#[test]
+fn test_max_pow10_no_more_than() {
+ let mut prevtenk = 1;
+ for k in 1..10 {
+ let tenk = prevtenk * 10;
+ assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk));
+ assert_eq!(max_pow10_no_more_than(tenk), (k, tenk));
+ prevtenk = tenk;
+ }
+}
+
+#[test]
+fn shortest_sanity_test() {
+ f64_shortest_sanity_test(format_shortest);
+ f32_shortest_sanity_test(format_shortest);
+ more_shortest_sanity_test(format_shortest);
+}
+
+#[test]
+#[cfg_attr(miri, ignore)] // Miri is too slow
+fn exact_sanity_test() {
+ // See comments in dragon.rs's exact_sanity_test for why this test is
+ // ignored on MSVC
+ if !cfg!(target_env = "msvc") {
+ f64_exact_sanity_test(format_exact);
+ }
+ f32_exact_sanity_test(format_exact);
+}
+
+#[test]
+fn test_to_shortest_str() {
+ to_shortest_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_shortest_exp_str() {
+ to_shortest_exp_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_exact_exp_str() {
+ to_exact_exp_str_test(format_exact);
+}
+
+#[test]
+fn test_to_exact_fixed_str() {
+ to_exact_fixed_str_test(format_exact);
+}
diff --git a/library/core/tests/num/i128.rs b/library/core/tests/num/i128.rs
new file mode 100644
index 000000000..1ddd20f33
--- /dev/null
+++ b/library/core/tests/num/i128.rs
@@ -0,0 +1 @@
+int_module!(i128);
diff --git a/library/core/tests/num/i16.rs b/library/core/tests/num/i16.rs
new file mode 100644
index 000000000..c7aa9fff9
--- /dev/null
+++ b/library/core/tests/num/i16.rs
@@ -0,0 +1 @@
+int_module!(i16);
diff --git a/library/core/tests/num/i32.rs b/library/core/tests/num/i32.rs
new file mode 100644
index 000000000..efd5b1596
--- /dev/null
+++ b/library/core/tests/num/i32.rs
@@ -0,0 +1,30 @@
+int_module!(i32);
+
+#[test]
+fn test_arith_operation() {
+ let a: isize = 10;
+ assert_eq!(a * (a - 1), 90);
+ let i32_a: isize = 10;
+ assert_eq!(i32_a, 10);
+ assert_eq!(i32_a - 10, 0);
+ assert_eq!(i32_a / 10, 1);
+ assert_eq!(i32_a - 20, -10);
+ assert_eq!(i32_a << 10, 10240);
+ assert_eq!(i32_a << 16, 655360);
+ assert_eq!(i32_a * 16, 160);
+ assert_eq!(i32_a * i32_a * i32_a, 1000);
+ assert_eq!(i32_a * i32_a * i32_a * i32_a, 10000);
+ assert_eq!(i32_a * i32_a / i32_a * i32_a, 100);
+ assert_eq!(i32_a * (i32_a - 1) << (2 + i32_a as usize), 368640);
+ let i32_b: isize = 0x10101010;
+ assert_eq!(i32_b + 1 - 1, i32_b);
+ assert_eq!(i32_b << 1, i32_b << 1);
+ assert_eq!(i32_b >> 1, i32_b >> 1);
+ assert_eq!(i32_b & i32_b << 1, 0);
+ assert_eq!(i32_b | i32_b << 1, 0x30303030);
+ let i32_c: isize = 0x10101010;
+ assert_eq!(
+ i32_c + i32_c * 2 / 3 * 2 + (i32_c - 7 % 3),
+ i32_c + i32_c * 2 / 3 * 2 + (i32_c - 7 % 3)
+ );
+}
diff --git a/library/core/tests/num/i64.rs b/library/core/tests/num/i64.rs
new file mode 100644
index 000000000..93d23c10a
--- /dev/null
+++ b/library/core/tests/num/i64.rs
@@ -0,0 +1 @@
+int_module!(i64);
diff --git a/library/core/tests/num/i8.rs b/library/core/tests/num/i8.rs
new file mode 100644
index 000000000..887d4f17d
--- /dev/null
+++ b/library/core/tests/num/i8.rs
@@ -0,0 +1 @@
+int_module!(i8);
diff --git a/library/core/tests/num/ieee754.rs b/library/core/tests/num/ieee754.rs
new file mode 100644
index 000000000..f6e5dfc98
--- /dev/null
+++ b/library/core/tests/num/ieee754.rs
@@ -0,0 +1,158 @@
+//! IEEE 754 floating point compliance tests
+//!
+//! To understand IEEE 754's requirements on a programming language, one must understand that the
+//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any
+//! one component. That means the hardware, language, and even libraries are considered part of
+//! conforming floating point support in a programming environment.
+//!
+//! A programming language's duty, accordingly, is:
+//! 1. offer access to the hardware where the hardware offers support
+//! 2. provide operations that fulfill the remaining requirements of the standard
+//! 3. provide the ability to write additional software that can fulfill those requirements
+//!
+//! This may be fulfilled in any combination that the language sees fit. However, to claim that
+//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without
+//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined
+//! as complete support for at least one specified floating point type as an "arithmetic" and
+//! "interchange" format, plus specified type conversions to "external character sequences" and
+//! integer types.
+//!
+//! For our purposes,
+//! "interchange format" => f32, f64
+//! "arithmetic format" => f32, f64, and any "soft floats"
+//! "external character sequence" => str from any float
+//! "integer format" => {i,u}{8,16,32,64,128}
+//!
+//! None of these tests are against Rust's own implementation. They are only tests against the
+//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests.
+//! Please consider this carefully when adding, removing, or reorganizing these tests. They are
+//! here so that it is clear what tests are required by the standard and what can be changed.
+use ::core::str::FromStr;
+
+// IEEE 754 for many tests is applied to specific bit patterns.
+// These generally are not applicable to NaN, however.
+macro_rules! assert_biteq {
+ ($lhs:expr, $rhs:expr) => {
+ assert_eq!($lhs.to_bits(), $rhs.to_bits())
+ };
+}
+
+// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts
+// of the formatting infrastructure, which makes it ideal for testing here.
+#[allow(unused_macros)]
+macro_rules! roundtrip {
+ ($f:expr => $t:ty) => {
+ ($f).to_string().parse::<$t>().unwrap()
+ };
+}
+
+macro_rules! assert_floats_roundtrip {
+ ($f:ident) => {
+ assert_biteq!(f32::$f, roundtrip!(f32::$f => f32));
+ assert_biteq!(f64::$f, roundtrip!(f64::$f => f64));
+ };
+ ($f:expr) => {
+ assert_biteq!($f as f32, roundtrip!($f => f32));
+ assert_biteq!($f as f64, roundtrip!($f => f64));
+ }
+}
+
+macro_rules! assert_floats_bitne {
+ ($lhs:ident, $rhs:ident) => {
+ assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits());
+ assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits());
+ };
+ ($lhs:expr, $rhs:expr) => {
+ assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs));
+ assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs));
+ };
+}
+
+// We must preserve signs on all numbers. That includes zero.
+// -0 and 0 are == normally, so test bit equality.
+#[test]
+fn preserve_signed_zero() {
+ assert_floats_roundtrip!(-0.0);
+ assert_floats_roundtrip!(0.0);
+ assert_floats_bitne!(0.0, -0.0);
+}
+
+#[test]
+fn preserve_signed_infinity() {
+ assert_floats_roundtrip!(INFINITY);
+ assert_floats_roundtrip!(NEG_INFINITY);
+ assert_floats_bitne!(INFINITY, NEG_INFINITY);
+}
+
+#[test]
+fn infinity_to_str() {
+ assert!(match f32::INFINITY.to_string().to_lowercase().as_str() {
+ "+infinity" | "infinity" => true,
+ "+inf" | "inf" => true,
+ _ => false,
+ });
+ assert!(
+ match f64::INFINITY.to_string().to_lowercase().as_str() {
+ "+infinity" | "infinity" => true,
+ "+inf" | "inf" => true,
+ _ => false,
+ },
+ "Infinity must write to a string as some casing of inf or infinity, with an optional +."
+ );
+}
+
+#[test]
+fn neg_infinity_to_str() {
+ assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() {
+ "-infinity" | "-inf" => true,
+ _ => false,
+ });
+ assert!(
+ match f64::NEG_INFINITY.to_string().to_lowercase().as_str() {
+ "-infinity" | "-inf" => true,
+ _ => false,
+ },
+ "Negative Infinity must write to a string as some casing of -inf or -infinity"
+ )
+}
+
+#[test]
+fn nan_to_str() {
+ assert!(
+ match f32::NAN.to_string().to_lowercase().as_str() {
+ "nan" | "+nan" | "-nan" => true,
+ _ => false,
+ },
+ "NaNs must write to a string as some casing of nan."
+ )
+}
+
+// "+"?("inf"|"infinity") in any case => Infinity
+#[test]
+fn infinity_from_str() {
+ assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap());
+ assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap());
+ assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap());
+ assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap());
+ // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd
+ assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap());
+}
+
+// "-inf"|"-infinity" in any case => Negative Infinity
+#[test]
+fn neg_infinity_from_str() {
+ assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap());
+ assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap());
+ assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap());
+ assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap());
+}
+
+// ("+"|"-"")?"s"?"nan" in any case => qNaN
+#[test]
+fn qnan_from_str() {
+ assert!("nan".parse::<f32>().unwrap().is_nan());
+ assert!("-nan".parse::<f32>().unwrap().is_nan());
+ assert!("+nan".parse::<f32>().unwrap().is_nan());
+ assert!("+NAN".parse::<f32>().unwrap().is_nan());
+ assert!("-NaN".parse::<f32>().unwrap().is_nan());
+}
diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs
new file mode 100644
index 000000000..dc3092e14
--- /dev/null
+++ b/library/core/tests/num/int_log.rs
@@ -0,0 +1,166 @@
+//! This tests the `Integer::{log,log2,log10}` methods. These tests are in a
+//! separate file because there's both a large number of them, and not all tests
+//! can be run on Android. This is because in Android `log2` uses an imprecise
+//! approximation:https://github.com/rust-lang/rust/blob/4825e12fc9c79954aa0fe18f5521efa6c19c7539/src/libstd/sys/unix/android.rs#L27-L53
+
+#[test]
+fn checked_log() {
+ assert_eq!(999u32.checked_log(10), Some(2));
+ assert_eq!(1000u32.checked_log(10), Some(3));
+ assert_eq!(555u32.checked_log(13), Some(2));
+ assert_eq!(63u32.checked_log(4), Some(2));
+ assert_eq!(64u32.checked_log(4), Some(3));
+ assert_eq!(10460353203u64.checked_log(3), Some(21));
+ assert_eq!(10460353202u64.checked_log(3), Some(20));
+ assert_eq!(147808829414345923316083210206383297601u128.checked_log(3), Some(80));
+ assert_eq!(147808829414345923316083210206383297600u128.checked_log(3), Some(79));
+ assert_eq!(22528399544939174411840147874772641u128.checked_log(19683), Some(8));
+ assert_eq!(22528399544939174411840147874772631i128.checked_log(19683), Some(7));
+
+ assert_eq!(0u8.checked_log(4), None);
+ assert_eq!(0u16.checked_log(4), None);
+ assert_eq!(0i8.checked_log(4), None);
+ assert_eq!(0i16.checked_log(4), None);
+
+ #[cfg(not(miri))] // Miri is too slow
+ for i in i16::MIN..=0 {
+ assert_eq!(i.checked_log(4), None);
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=i16::MAX {
+ assert_eq!(i.checked_log(13), Some((i as f32).log(13.0) as u32));
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=u16::MAX {
+ assert_eq!(i.checked_log(13), Some((i as f32).log(13.0) as u32));
+ }
+}
+
+#[test]
+fn checked_log2() {
+ assert_eq!(5u32.checked_log2(), Some(2));
+ assert_eq!(0u64.checked_log2(), None);
+ assert_eq!(128i32.checked_log2(), Some(7));
+ assert_eq!((-55i16).checked_log2(), None);
+
+ assert_eq!(0u8.checked_log2(), None);
+ assert_eq!(0u16.checked_log2(), None);
+ assert_eq!(0i8.checked_log2(), None);
+ assert_eq!(0i16.checked_log2(), None);
+
+ for i in 1..=u8::MAX {
+ assert_eq!(i.checked_log2(), Some((i as f32).log2() as u32));
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=u16::MAX {
+ // Guard against Android's imprecise f32::log2 implementation.
+ if i != 8192 && i != 32768 {
+ assert_eq!(i.checked_log2(), Some((i as f32).log2() as u32));
+ }
+ }
+ for i in i8::MIN..=0 {
+ assert_eq!(i.checked_log2(), None);
+ }
+ for i in 1..=i8::MAX {
+ assert_eq!(i.checked_log2(), Some((i as f32).log2() as u32));
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in i16::MIN..=0 {
+ assert_eq!(i.checked_log2(), None);
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=i16::MAX {
+ // Guard against Android's imprecise f32::log2 implementation.
+ if i != 8192 {
+ assert_eq!(i.checked_log2(), Some((i as f32).log2() as u32));
+ }
+ }
+}
+
+// Validate cases that fail on Android's imprecise float log2 implementation.
+#[test]
+#[cfg(not(target_os = "android"))]
+fn checked_log2_not_android() {
+ assert_eq!(8192u16.checked_log2(), Some((8192f32).log2() as u32));
+ assert_eq!(32768u16.checked_log2(), Some((32768f32).log2() as u32));
+ assert_eq!(8192i16.checked_log2(), Some((8192f32).log2() as u32));
+}
+
+#[test]
+fn checked_log10() {
+ assert_eq!(0u8.checked_log10(), None);
+ assert_eq!(0u16.checked_log10(), None);
+ assert_eq!(0i8.checked_log10(), None);
+ assert_eq!(0i16.checked_log10(), None);
+
+ #[cfg(not(miri))] // Miri is too slow
+ for i in i16::MIN..=0 {
+ assert_eq!(i.checked_log10(), None);
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=i16::MAX {
+ assert_eq!(i.checked_log10(), Some((i as f32).log10() as u32));
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=u16::MAX {
+ assert_eq!(i.checked_log10(), Some((i as f32).log10() as u32));
+ }
+ #[cfg(not(miri))] // Miri is too slow
+ for i in 1..=100_000u32 {
+ assert_eq!(i.checked_log10(), Some((i as f32).log10() as u32));
+ }
+}
+
+macro_rules! log10_loop {
+ ($T:ty, $log10_max:expr) => {
+ assert_eq!(<$T>::MAX.log10(), $log10_max);
+ for i in 0..=$log10_max {
+ let p = (10 as $T).pow(i as u32);
+ if p >= 10 {
+ assert_eq!((p - 9).log10(), i - 1);
+ assert_eq!((p - 1).log10(), i - 1);
+ }
+ assert_eq!(p.log10(), i);
+ assert_eq!((p + 1).log10(), i);
+ if p >= 10 {
+ assert_eq!((p + 9).log10(), i);
+ }
+
+ // also check `x.log(10)`
+ if p >= 10 {
+ assert_eq!((p - 9).log(10), i - 1);
+ assert_eq!((p - 1).log(10), i - 1);
+ }
+ assert_eq!(p.log(10), i);
+ assert_eq!((p + 1).log(10), i);
+ if p >= 10 {
+ assert_eq!((p + 9).log(10), i);
+ }
+ }
+ };
+}
+
+#[test]
+fn log10_u8() {
+ log10_loop! { u8, 2 }
+}
+
+#[test]
+fn log10_u16() {
+ log10_loop! { u16, 4 }
+}
+
+#[test]
+fn log10_u32() {
+ log10_loop! { u32, 9 }
+}
+
+#[test]
+fn log10_u64() {
+ log10_loop! { u64, 19 }
+}
+
+#[test]
+fn log10_u128() {
+ log10_loop! { u128, 38 }
+}
diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs
new file mode 100644
index 000000000..8b84a78e6
--- /dev/null
+++ b/library/core/tests/num/int_macros.rs
@@ -0,0 +1,343 @@
+macro_rules! int_module {
+ ($T:ident) => {
+ #[cfg(test)]
+ mod tests {
+ use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
+ use core::$T::*;
+
+ use crate::num;
+
+ #[test]
+ fn test_overflows() {
+ assert!(MAX > 0);
+ assert!(MIN <= 0);
+ assert_eq!(MIN + MAX + 1, 0);
+ }
+
+ #[test]
+ fn test_num() {
+ num::test_num(10 as $T, 2 as $T);
+ }
+
+ #[test]
+ fn test_rem_euclid() {
+ assert_eq!((-1 as $T).rem_euclid(MIN), MAX);
+ }
+
+ #[test]
+ pub fn test_abs() {
+ assert_eq!((1 as $T).abs(), 1 as $T);
+ assert_eq!((0 as $T).abs(), 0 as $T);
+ assert_eq!((-1 as $T).abs(), 1 as $T);
+ }
+
+ #[test]
+ fn test_signum() {
+ assert_eq!((1 as $T).signum(), 1 as $T);
+ assert_eq!((0 as $T).signum(), 0 as $T);
+ assert_eq!((-0 as $T).signum(), 0 as $T);
+ assert_eq!((-1 as $T).signum(), -1 as $T);
+ }
+
+ #[test]
+ fn test_is_positive() {
+ assert!((1 as $T).is_positive());
+ assert!(!(0 as $T).is_positive());
+ assert!(!(-0 as $T).is_positive());
+ assert!(!(-1 as $T).is_positive());
+ }
+
+ #[test]
+ fn test_is_negative() {
+ assert!(!(1 as $T).is_negative());
+ assert!(!(0 as $T).is_negative());
+ assert!(!(-0 as $T).is_negative());
+ assert!((-1 as $T).is_negative());
+ }
+
+ #[test]
+ fn test_bitwise_operators() {
+ assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(0b1010 as $T));
+ assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(0b1010 as $T));
+ assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(0b1010 as $T));
+ assert_eq!(0b1110 as $T, (0b0111 as $T).shl(1));
+ assert_eq!(0b0111 as $T, (0b1110 as $T).shr(1));
+ assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not());
+ }
+
+ const A: $T = 0b0101100;
+ const B: $T = 0b0100001;
+ const C: $T = 0b1111001;
+
+ const _0: $T = 0;
+ const _1: $T = !0;
+
+ #[test]
+ fn test_count_ones() {
+ assert_eq!(A.count_ones(), 3);
+ assert_eq!(B.count_ones(), 2);
+ assert_eq!(C.count_ones(), 5);
+ }
+
+ #[test]
+ fn test_count_zeros() {
+ assert_eq!(A.count_zeros(), $T::BITS - 3);
+ assert_eq!(B.count_zeros(), $T::BITS - 2);
+ assert_eq!(C.count_zeros(), $T::BITS - 5);
+ }
+
+ #[test]
+ fn test_leading_trailing_ones() {
+ let a: $T = 0b0101_1111;
+ assert_eq!(a.trailing_ones(), 5);
+ assert_eq!((!a).leading_ones(), $T::BITS - 7);
+
+ assert_eq!(a.reverse_bits().leading_ones(), 5);
+
+ assert_eq!(_1.leading_ones(), $T::BITS);
+ assert_eq!(_1.trailing_ones(), $T::BITS);
+
+ assert_eq!((_1 << 1).trailing_ones(), 0);
+ assert_eq!(MAX.leading_ones(), 0);
+
+ assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
+ assert_eq!(MAX.trailing_ones(), $T::BITS - 1);
+
+ assert_eq!(_0.leading_ones(), 0);
+ assert_eq!(_0.trailing_ones(), 0);
+
+ let x: $T = 0b0010_1100;
+ assert_eq!(x.leading_ones(), 0);
+ assert_eq!(x.trailing_ones(), 0);
+ }
+
+ #[test]
+ fn test_rotate() {
+ assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
+ assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
+ assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
+
+ // Rotating these should make no difference
+ //
+ // We test using 124 bits because to ensure that overlong bit shifts do
+ // not cause undefined behaviour. See #10183.
+ assert_eq!(_0.rotate_left(124), _0);
+ assert_eq!(_1.rotate_left(124), _1);
+ assert_eq!(_0.rotate_right(124), _0);
+ assert_eq!(_1.rotate_right(124), _1);
+
+ // Rotating by 0 should have no effect
+ assert_eq!(A.rotate_left(0), A);
+ assert_eq!(B.rotate_left(0), B);
+ assert_eq!(C.rotate_left(0), C);
+ // Rotating by a multiple of word size should also have no effect
+ assert_eq!(A.rotate_left(128), A);
+ assert_eq!(B.rotate_left(128), B);
+ assert_eq!(C.rotate_left(128), C);
+ }
+
+ #[test]
+ fn test_swap_bytes() {
+ assert_eq!(A.swap_bytes().swap_bytes(), A);
+ assert_eq!(B.swap_bytes().swap_bytes(), B);
+ assert_eq!(C.swap_bytes().swap_bytes(), C);
+
+ // Swapping these should make no difference
+ assert_eq!(_0.swap_bytes(), _0);
+ assert_eq!(_1.swap_bytes(), _1);
+ }
+
+ #[test]
+ fn test_le() {
+ assert_eq!($T::from_le(A.to_le()), A);
+ assert_eq!($T::from_le(B.to_le()), B);
+ assert_eq!($T::from_le(C.to_le()), C);
+ assert_eq!($T::from_le(_0), _0);
+ assert_eq!($T::from_le(_1), _1);
+ assert_eq!(_0.to_le(), _0);
+ assert_eq!(_1.to_le(), _1);
+ }
+
+ #[test]
+ fn test_be() {
+ assert_eq!($T::from_be(A.to_be()), A);
+ assert_eq!($T::from_be(B.to_be()), B);
+ assert_eq!($T::from_be(C.to_be()), C);
+ assert_eq!($T::from_be(_0), _0);
+ assert_eq!($T::from_be(_1), _1);
+ assert_eq!(_0.to_be(), _0);
+ assert_eq!(_1.to_be(), _1);
+ }
+
+ #[test]
+ fn test_signed_checked_div() {
+ assert_eq!((10 as $T).checked_div(2), Some(5));
+ assert_eq!((5 as $T).checked_div(0), None);
+ assert_eq!(isize::MIN.checked_div(-1), None);
+ }
+
+ #[test]
+ fn test_saturating_abs() {
+ assert_eq!((0 as $T).saturating_abs(), 0);
+ assert_eq!((123 as $T).saturating_abs(), 123);
+ assert_eq!((-123 as $T).saturating_abs(), 123);
+ assert_eq!((MAX - 2).saturating_abs(), MAX - 2);
+ assert_eq!((MAX - 1).saturating_abs(), MAX - 1);
+ assert_eq!(MAX.saturating_abs(), MAX);
+ assert_eq!((MIN + 2).saturating_abs(), MAX - 1);
+ assert_eq!((MIN + 1).saturating_abs(), MAX);
+ assert_eq!(MIN.saturating_abs(), MAX);
+ }
+
+ #[test]
+ fn test_saturating_neg() {
+ assert_eq!((0 as $T).saturating_neg(), 0);
+ assert_eq!((123 as $T).saturating_neg(), -123);
+ assert_eq!((-123 as $T).saturating_neg(), 123);
+ assert_eq!((MAX - 2).saturating_neg(), MIN + 3);
+ assert_eq!((MAX - 1).saturating_neg(), MIN + 2);
+ assert_eq!(MAX.saturating_neg(), MIN + 1);
+ assert_eq!((MIN + 2).saturating_neg(), MAX - 1);
+ assert_eq!((MIN + 1).saturating_neg(), MAX);
+ assert_eq!(MIN.saturating_neg(), MAX);
+ }
+
+ #[test]
+ fn test_from_str() {
+ fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> {
+ std::str::FromStr::from_str(t).ok()
+ }
+ assert_eq!(from_str::<$T>("0"), Some(0 as $T));
+ assert_eq!(from_str::<$T>("3"), Some(3 as $T));
+ assert_eq!(from_str::<$T>("10"), Some(10 as $T));
+ assert_eq!(from_str::<i32>("123456789"), Some(123456789 as i32));
+ assert_eq!(from_str::<$T>("00100"), Some(100 as $T));
+
+ assert_eq!(from_str::<$T>("-1"), Some(-1 as $T));
+ assert_eq!(from_str::<$T>("-3"), Some(-3 as $T));
+ assert_eq!(from_str::<$T>("-10"), Some(-10 as $T));
+ assert_eq!(from_str::<i32>("-123456789"), Some(-123456789 as i32));
+ assert_eq!(from_str::<$T>("-00100"), Some(-100 as $T));
+
+ assert_eq!(from_str::<$T>(""), None);
+ assert_eq!(from_str::<$T>(" "), None);
+ assert_eq!(from_str::<$T>("x"), None);
+ }
+
+ #[test]
+ fn test_from_str_radix() {
+ assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T));
+ assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T));
+ assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T));
+ assert_eq!(i32::from_str_radix("123", 16), Ok(291 as i32));
+ assert_eq!(i32::from_str_radix("ffff", 16), Ok(65535 as i32));
+ assert_eq!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32));
+ assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T));
+ assert_eq!($T::from_str_radix("Z", 36), Ok(35 as $T));
+
+ assert_eq!($T::from_str_radix("-123", 10), Ok(-123 as $T));
+ assert_eq!($T::from_str_radix("-1001", 2), Ok(-9 as $T));
+ assert_eq!($T::from_str_radix("-123", 8), Ok(-83 as $T));
+ assert_eq!(i32::from_str_radix("-123", 16), Ok(-291 as i32));
+ assert_eq!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32));
+ assert_eq!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32));
+ assert_eq!($T::from_str_radix("-z", 36), Ok(-35 as $T));
+ assert_eq!($T::from_str_radix("-Z", 36), Ok(-35 as $T));
+
+ assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>);
+ assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>);
+ }
+
+ #[test]
+ fn test_pow() {
+ let mut r = 2 as $T;
+ assert_eq!(r.pow(2), 4 as $T);
+ assert_eq!(r.pow(0), 1 as $T);
+ assert_eq!(r.wrapping_pow(2), 4 as $T);
+ assert_eq!(r.wrapping_pow(0), 1 as $T);
+ assert_eq!(r.checked_pow(2), Some(4 as $T));
+ assert_eq!(r.checked_pow(0), Some(1 as $T));
+ assert_eq!(r.overflowing_pow(2), (4 as $T, false));
+ assert_eq!(r.overflowing_pow(0), (1 as $T, false));
+ assert_eq!(r.saturating_pow(2), 4 as $T);
+ assert_eq!(r.saturating_pow(0), 1 as $T);
+
+ r = MAX;
+ // use `^` to represent .pow() with no overflow.
+ // if itest::MAX == 2^j-1, then itest is a `j` bit int,
+ // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
+ // thussaturating_pow the overflowing result is exactly 1.
+ assert_eq!(r.wrapping_pow(2), 1 as $T);
+ assert_eq!(r.checked_pow(2), None);
+ assert_eq!(r.overflowing_pow(2), (1 as $T, true));
+ assert_eq!(r.saturating_pow(2), MAX);
+ //test for negative exponent.
+ r = -2 as $T;
+ assert_eq!(r.pow(2), 4 as $T);
+ assert_eq!(r.pow(3), -8 as $T);
+ assert_eq!(r.pow(0), 1 as $T);
+ assert_eq!(r.wrapping_pow(2), 4 as $T);
+ assert_eq!(r.wrapping_pow(3), -8 as $T);
+ assert_eq!(r.wrapping_pow(0), 1 as $T);
+ assert_eq!(r.checked_pow(2), Some(4 as $T));
+ assert_eq!(r.checked_pow(3), Some(-8 as $T));
+ assert_eq!(r.checked_pow(0), Some(1 as $T));
+ assert_eq!(r.overflowing_pow(2), (4 as $T, false));
+ assert_eq!(r.overflowing_pow(3), (-8 as $T, false));
+ assert_eq!(r.overflowing_pow(0), (1 as $T, false));
+ assert_eq!(r.saturating_pow(2), 4 as $T);
+ assert_eq!(r.saturating_pow(3), -8 as $T);
+ assert_eq!(r.saturating_pow(0), 1 as $T);
+ }
+
+ #[test]
+ fn test_div_floor() {
+ let a: $T = 8;
+ let b = 3;
+ assert_eq!(a.div_floor(b), 2);
+ assert_eq!(a.div_floor(-b), -3);
+ assert_eq!((-a).div_floor(b), -3);
+ assert_eq!((-a).div_floor(-b), 2);
+ }
+
+ #[test]
+ fn test_div_ceil() {
+ let a: $T = 8;
+ let b = 3;
+ assert_eq!(a.div_ceil(b), 3);
+ assert_eq!(a.div_ceil(-b), -2);
+ assert_eq!((-a).div_ceil(b), -2);
+ assert_eq!((-a).div_ceil(-b), 3);
+ }
+
+ #[test]
+ fn test_next_multiple_of() {
+ assert_eq!((16 as $T).next_multiple_of(8), 16);
+ assert_eq!((23 as $T).next_multiple_of(8), 24);
+ assert_eq!((16 as $T).next_multiple_of(-8), 16);
+ assert_eq!((23 as $T).next_multiple_of(-8), 16);
+ assert_eq!((-16 as $T).next_multiple_of(8), -16);
+ assert_eq!((-23 as $T).next_multiple_of(8), -16);
+ assert_eq!((-16 as $T).next_multiple_of(-8), -16);
+ assert_eq!((-23 as $T).next_multiple_of(-8), -24);
+ assert_eq!(MIN.next_multiple_of(-1), MIN);
+ }
+
+ #[test]
+ fn test_checked_next_multiple_of() {
+ assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16));
+ assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24));
+ assert_eq!((16 as $T).checked_next_multiple_of(-8), Some(16));
+ assert_eq!((23 as $T).checked_next_multiple_of(-8), Some(16));
+ assert_eq!((-16 as $T).checked_next_multiple_of(8), Some(-16));
+ assert_eq!((-23 as $T).checked_next_multiple_of(8), Some(-16));
+ assert_eq!((-16 as $T).checked_next_multiple_of(-8), Some(-16));
+ assert_eq!((-23 as $T).checked_next_multiple_of(-8), Some(-24));
+ assert_eq!((1 as $T).checked_next_multiple_of(0), None);
+ assert_eq!(MAX.checked_next_multiple_of(2), None);
+ assert_eq!(MIN.checked_next_multiple_of(-3), None);
+ assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN));
+ }
+ }
+ };
+}
diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs
new file mode 100644
index 000000000..49580cdcc
--- /dev/null
+++ b/library/core/tests/num/mod.rs
@@ -0,0 +1,871 @@
+use core::cmp::PartialEq;
+use core::convert::{TryFrom, TryInto};
+use core::fmt::Debug;
+use core::marker::Copy;
+use core::num::{can_not_overflow, IntErrorKind, ParseIntError, TryFromIntError};
+use core::ops::{Add, Div, Mul, Rem, Sub};
+use core::option::Option;
+use core::option::Option::None;
+use core::str::FromStr;
+
+#[macro_use]
+mod int_macros;
+
+mod i128;
+mod i16;
+mod i32;
+mod i64;
+mod i8;
+
+#[macro_use]
+mod uint_macros;
+
+mod u128;
+mod u16;
+mod u32;
+mod u64;
+mod u8;
+
+mod bignum;
+
+mod const_from;
+mod dec2flt;
+mod flt2dec;
+mod int_log;
+mod ops;
+mod wrapping;
+
+mod ieee754;
+mod nan;
+
+/// Adds the attribute to all items in the block.
+macro_rules! cfg_block {
+ ($(#[$attr:meta]{$($it:item)*})*) => {$($(
+ #[$attr]
+ $it
+ )*)*}
+}
+
+/// Groups items that assume the pointer width is either 16/32/64, and has to be altered if
+/// support for larger/smaller pointer widths are added in the future.
+macro_rules! assume_usize_width {
+ {$($it:item)*} => {#[cfg(not(any(
+ target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
+ compile_error!("The current tests of try_from on usize/isize assume that \
+ the pointer width is either 16, 32, or 64");
+ $($it)*
+ }
+}
+
+/// Helper function for testing numeric operations
+pub fn test_num<T>(ten: T, two: T)
+where
+ T: PartialEq
+ + Add<Output = T>
+ + Sub<Output = T>
+ + Mul<Output = T>
+ + Div<Output = T>
+ + Rem<Output = T>
+ + Debug
+ + Copy,
+{
+ assert_eq!(ten.add(two), ten + two);
+ assert_eq!(ten.sub(two), ten - two);
+ assert_eq!(ten.mul(two), ten * two);
+ assert_eq!(ten.div(two), ten / two);
+ assert_eq!(ten.rem(two), ten % two);
+}
+
+/// Helper function for asserting number parsing returns a specific error
+fn test_parse<T>(num_str: &str, expected: Result<T, IntErrorKind>)
+where
+ T: FromStr<Err = ParseIntError>,
+ Result<T, IntErrorKind>: PartialEq + Debug,
+{
+ assert_eq!(num_str.parse::<T>().map_err(|e| e.kind().clone()), expected)
+}
+
+#[test]
+fn from_str_issue7588() {
+ let u: Option<u8> = u8::from_str_radix("1000", 10).ok();
+ assert_eq!(u, None);
+ let s: Option<i16> = i16::from_str_radix("80000", 10).ok();
+ assert_eq!(s, None);
+}
+
+#[test]
+fn test_int_from_str_overflow() {
+ test_parse::<i8>("127", Ok(127));
+ test_parse::<i8>("128", Err(IntErrorKind::PosOverflow));
+
+ test_parse::<i8>("-128", Ok(-128));
+ test_parse::<i8>("-129", Err(IntErrorKind::NegOverflow));
+
+ test_parse::<i16>("32767", Ok(32_767));
+ test_parse::<i16>("32768", Err(IntErrorKind::PosOverflow));
+
+ test_parse::<i16>("-32768", Ok(-32_768));
+ test_parse::<i16>("-32769", Err(IntErrorKind::NegOverflow));
+
+ test_parse::<i32>("2147483647", Ok(2_147_483_647));
+ test_parse::<i32>("2147483648", Err(IntErrorKind::PosOverflow));
+
+ test_parse::<i32>("-2147483648", Ok(-2_147_483_648));
+ test_parse::<i32>("-2147483649", Err(IntErrorKind::NegOverflow));
+
+ test_parse::<i64>("9223372036854775807", Ok(9_223_372_036_854_775_807));
+ test_parse::<i64>("9223372036854775808", Err(IntErrorKind::PosOverflow));
+
+ test_parse::<i64>("-9223372036854775808", Ok(-9_223_372_036_854_775_808));
+ test_parse::<i64>("-9223372036854775809", Err(IntErrorKind::NegOverflow));
+}
+
+#[test]
+fn test_can_not_overflow() {
+ fn can_overflow<T>(radix: u32, input: &str) -> bool
+ where
+ T: std::convert::TryFrom<i8>,
+ {
+ !can_not_overflow::<T>(radix, T::try_from(-1_i8).is_ok(), input.as_bytes())
+ }
+
+ // Positive tests:
+ assert!(!can_overflow::<i8>(16, "F"));
+ assert!(!can_overflow::<u8>(16, "FF"));
+
+ assert!(!can_overflow::<i8>(10, "9"));
+ assert!(!can_overflow::<u8>(10, "99"));
+
+ // Negative tests:
+
+ // Not currently in std lib (issue: #27728)
+ fn format_radix<T>(mut x: T, radix: T) -> String
+ where
+ T: std::ops::Rem<Output = T>,
+ T: std::ops::Div<Output = T>,
+ T: std::cmp::PartialEq,
+ T: std::default::Default,
+ T: Copy,
+ T: Default,
+ u32: TryFrom<T>,
+ {
+ let mut result = vec![];
+
+ loop {
+ let m = x % radix;
+ x = x / radix;
+ result.push(
+ std::char::from_digit(m.try_into().ok().unwrap(), radix.try_into().ok().unwrap())
+ .unwrap(),
+ );
+ if x == T::default() {
+ break;
+ }
+ }
+ result.into_iter().rev().collect()
+ }
+
+ macro_rules! check {
+ ($($t:ty)*) => ($(
+ for base in 2..=36 {
+ let num = (<$t>::MAX as u128) + 1;
+
+ // Calcutate the string length for the smallest overflowing number:
+ let max_len_string = format_radix(num, base as u128);
+ // Ensure that that string length is deemed to potentially overflow:
+ assert!(can_overflow::<$t>(base, &max_len_string));
+ }
+ )*)
+ }
+
+ check! { i8 i16 i32 i64 i128 isize usize u8 u16 u32 u64 }
+
+ // Check u128 separately:
+ for base in 2..=36 {
+ let num = u128::MAX as u128;
+ let max_len_string = format_radix(num, base as u128);
+ // base 16 fits perfectly for u128 and won't overflow:
+ assert_eq!(can_overflow::<u128>(base, &max_len_string), base != 16);
+ }
+}
+
+#[test]
+fn test_leading_plus() {
+ test_parse::<u8>("+127", Ok(127));
+ test_parse::<i64>("+9223372036854775807", Ok(9223372036854775807));
+}
+
+#[test]
+fn test_invalid() {
+ test_parse::<i8>("--129", Err(IntErrorKind::InvalidDigit));
+ test_parse::<i8>("++129", Err(IntErrorKind::InvalidDigit));
+ test_parse::<u8>("Съешь", Err(IntErrorKind::InvalidDigit));
+ test_parse::<u8>("123Hello", Err(IntErrorKind::InvalidDigit));
+ test_parse::<i8>("--", Err(IntErrorKind::InvalidDigit));
+ test_parse::<i8>("-", Err(IntErrorKind::InvalidDigit));
+ test_parse::<i8>("+", Err(IntErrorKind::InvalidDigit));
+ test_parse::<u8>("-1", Err(IntErrorKind::InvalidDigit));
+}
+
+#[test]
+fn test_empty() {
+ test_parse::<u8>("", Err(IntErrorKind::Empty));
+}
+
+#[test]
+fn test_infallible_try_from_int_error() {
+ let func = |x: i8| -> Result<i32, TryFromIntError> { Ok(x.try_into()?) };
+
+ assert!(func(0).is_ok());
+}
+
+macro_rules! test_impl_from {
+ ($fn_name:ident, bool, $target: ty) => {
+ #[test]
+ fn $fn_name() {
+ let one: $target = 1;
+ let zero: $target = 0;
+ assert_eq!(one, <$target>::from(true));
+ assert_eq!(zero, <$target>::from(false));
+ }
+ };
+ ($fn_name: ident, $Small: ty, $Large: ty) => {
+ #[test]
+ fn $fn_name() {
+ let small_max = <$Small>::MAX;
+ let small_min = <$Small>::MIN;
+ let large_max: $Large = small_max.into();
+ let large_min: $Large = small_min.into();
+ assert_eq!(large_max as $Small, small_max);
+ assert_eq!(large_min as $Small, small_min);
+ }
+ };
+}
+
+// Unsigned -> Unsigned
+test_impl_from! { test_u8u16, u8, u16 }
+test_impl_from! { test_u8u32, u8, u32 }
+test_impl_from! { test_u8u64, u8, u64 }
+test_impl_from! { test_u8usize, u8, usize }
+test_impl_from! { test_u16u32, u16, u32 }
+test_impl_from! { test_u16u64, u16, u64 }
+test_impl_from! { test_u32u64, u32, u64 }
+
+// Signed -> Signed
+test_impl_from! { test_i8i16, i8, i16 }
+test_impl_from! { test_i8i32, i8, i32 }
+test_impl_from! { test_i8i64, i8, i64 }
+test_impl_from! { test_i8isize, i8, isize }
+test_impl_from! { test_i16i32, i16, i32 }
+test_impl_from! { test_i16i64, i16, i64 }
+test_impl_from! { test_i32i64, i32, i64 }
+
+// Unsigned -> Signed
+test_impl_from! { test_u8i16, u8, i16 }
+test_impl_from! { test_u8i32, u8, i32 }
+test_impl_from! { test_u8i64, u8, i64 }
+test_impl_from! { test_u16i32, u16, i32 }
+test_impl_from! { test_u16i64, u16, i64 }
+test_impl_from! { test_u32i64, u32, i64 }
+
+// Bool -> Integer
+test_impl_from! { test_boolu8, bool, u8 }
+test_impl_from! { test_boolu16, bool, u16 }
+test_impl_from! { test_boolu32, bool, u32 }
+test_impl_from! { test_boolu64, bool, u64 }
+test_impl_from! { test_boolu128, bool, u128 }
+test_impl_from! { test_booli8, bool, i8 }
+test_impl_from! { test_booli16, bool, i16 }
+test_impl_from! { test_booli32, bool, i32 }
+test_impl_from! { test_booli64, bool, i64 }
+test_impl_from! { test_booli128, bool, i128 }
+
+// Signed -> Float
+test_impl_from! { test_i8f32, i8, f32 }
+test_impl_from! { test_i8f64, i8, f64 }
+test_impl_from! { test_i16f32, i16, f32 }
+test_impl_from! { test_i16f64, i16, f64 }
+test_impl_from! { test_i32f64, i32, f64 }
+
+// Unsigned -> Float
+test_impl_from! { test_u8f32, u8, f32 }
+test_impl_from! { test_u8f64, u8, f64 }
+test_impl_from! { test_u16f32, u16, f32 }
+test_impl_from! { test_u16f64, u16, f64 }
+test_impl_from! { test_u32f64, u32, f64 }
+
+// Float -> Float
+#[test]
+fn test_f32f64() {
+ let max: f64 = f32::MAX.into();
+ assert_eq!(max as f32, f32::MAX);
+ assert!(max.is_normal());
+
+ let min: f64 = f32::MIN.into();
+ assert_eq!(min as f32, f32::MIN);
+ assert!(min.is_normal());
+
+ let min_positive: f64 = f32::MIN_POSITIVE.into();
+ assert_eq!(min_positive as f32, f32::MIN_POSITIVE);
+ assert!(min_positive.is_normal());
+
+ let epsilon: f64 = f32::EPSILON.into();
+ assert_eq!(epsilon as f32, f32::EPSILON);
+ assert!(epsilon.is_normal());
+
+ let zero: f64 = (0.0f32).into();
+ assert_eq!(zero as f32, 0.0f32);
+ assert!(zero.is_sign_positive());
+
+ let neg_zero: f64 = (-0.0f32).into();
+ assert_eq!(neg_zero as f32, -0.0f32);
+ assert!(neg_zero.is_sign_negative());
+
+ let infinity: f64 = f32::INFINITY.into();
+ assert_eq!(infinity as f32, f32::INFINITY);
+ assert!(infinity.is_infinite());
+ assert!(infinity.is_sign_positive());
+
+ let neg_infinity: f64 = f32::NEG_INFINITY.into();
+ assert_eq!(neg_infinity as f32, f32::NEG_INFINITY);
+ assert!(neg_infinity.is_infinite());
+ assert!(neg_infinity.is_sign_negative());
+
+ let nan: f64 = f32::NAN.into();
+ assert!(nan.is_nan());
+}
+
+/// Conversions where the full width of $source can be represented as $target
+macro_rules! test_impl_try_from_always_ok {
+ ($fn_name:ident, $source:ty, $target: ty) => {
+ #[test]
+ fn $fn_name() {
+ let max = <$source>::MAX;
+ let min = <$source>::MIN;
+ let zero: $source = 0;
+ assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target);
+ assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target);
+ assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target);
+ }
+ };
+}
+
+test_impl_try_from_always_ok! { test_try_u8u8, u8, u8 }
+test_impl_try_from_always_ok! { test_try_u8u16, u8, u16 }
+test_impl_try_from_always_ok! { test_try_u8u32, u8, u32 }
+test_impl_try_from_always_ok! { test_try_u8u64, u8, u64 }
+test_impl_try_from_always_ok! { test_try_u8u128, u8, u128 }
+test_impl_try_from_always_ok! { test_try_u8i16, u8, i16 }
+test_impl_try_from_always_ok! { test_try_u8i32, u8, i32 }
+test_impl_try_from_always_ok! { test_try_u8i64, u8, i64 }
+test_impl_try_from_always_ok! { test_try_u8i128, u8, i128 }
+
+test_impl_try_from_always_ok! { test_try_u16u16, u16, u16 }
+test_impl_try_from_always_ok! { test_try_u16u32, u16, u32 }
+test_impl_try_from_always_ok! { test_try_u16u64, u16, u64 }
+test_impl_try_from_always_ok! { test_try_u16u128, u16, u128 }
+test_impl_try_from_always_ok! { test_try_u16i32, u16, i32 }
+test_impl_try_from_always_ok! { test_try_u16i64, u16, i64 }
+test_impl_try_from_always_ok! { test_try_u16i128, u16, i128 }
+
+test_impl_try_from_always_ok! { test_try_u32u32, u32, u32 }
+test_impl_try_from_always_ok! { test_try_u32u64, u32, u64 }
+test_impl_try_from_always_ok! { test_try_u32u128, u32, u128 }
+test_impl_try_from_always_ok! { test_try_u32i64, u32, i64 }
+test_impl_try_from_always_ok! { test_try_u32i128, u32, i128 }
+
+test_impl_try_from_always_ok! { test_try_u64u64, u64, u64 }
+test_impl_try_from_always_ok! { test_try_u64u128, u64, u128 }
+test_impl_try_from_always_ok! { test_try_u64i128, u64, i128 }
+
+test_impl_try_from_always_ok! { test_try_u128u128, u128, u128 }
+
+test_impl_try_from_always_ok! { test_try_i8i8, i8, i8 }
+test_impl_try_from_always_ok! { test_try_i8i16, i8, i16 }
+test_impl_try_from_always_ok! { test_try_i8i32, i8, i32 }
+test_impl_try_from_always_ok! { test_try_i8i64, i8, i64 }
+test_impl_try_from_always_ok! { test_try_i8i128, i8, i128 }
+
+test_impl_try_from_always_ok! { test_try_i16i16, i16, i16 }
+test_impl_try_from_always_ok! { test_try_i16i32, i16, i32 }
+test_impl_try_from_always_ok! { test_try_i16i64, i16, i64 }
+test_impl_try_from_always_ok! { test_try_i16i128, i16, i128 }
+
+test_impl_try_from_always_ok! { test_try_i32i32, i32, i32 }
+test_impl_try_from_always_ok! { test_try_i32i64, i32, i64 }
+test_impl_try_from_always_ok! { test_try_i32i128, i32, i128 }
+
+test_impl_try_from_always_ok! { test_try_i64i64, i64, i64 }
+test_impl_try_from_always_ok! { test_try_i64i128, i64, i128 }
+
+test_impl_try_from_always_ok! { test_try_i128i128, i128, i128 }
+
+test_impl_try_from_always_ok! { test_try_usizeusize, usize, usize }
+test_impl_try_from_always_ok! { test_try_isizeisize, isize, isize }
+
+assume_usize_width! {
+ test_impl_try_from_always_ok! { test_try_u8usize, u8, usize }
+ test_impl_try_from_always_ok! { test_try_u8isize, u8, isize }
+ test_impl_try_from_always_ok! { test_try_i8isize, i8, isize }
+
+ test_impl_try_from_always_ok! { test_try_u16usize, u16, usize }
+ test_impl_try_from_always_ok! { test_try_i16isize, i16, isize }
+
+ test_impl_try_from_always_ok! { test_try_usizeu64, usize, u64 }
+ test_impl_try_from_always_ok! { test_try_usizeu128, usize, u128 }
+ test_impl_try_from_always_ok! { test_try_usizei128, usize, i128 }
+
+ test_impl_try_from_always_ok! { test_try_isizei64, isize, i64 }
+ test_impl_try_from_always_ok! { test_try_isizei128, isize, i128 }
+
+ cfg_block!(
+ #[cfg(target_pointer_width = "16")] {
+ test_impl_try_from_always_ok! { test_try_usizeu16, usize, u16 }
+ test_impl_try_from_always_ok! { test_try_isizei16, isize, i16 }
+ test_impl_try_from_always_ok! { test_try_usizeu32, usize, u32 }
+ test_impl_try_from_always_ok! { test_try_usizei32, usize, i32 }
+ test_impl_try_from_always_ok! { test_try_isizei32, isize, i32 }
+ test_impl_try_from_always_ok! { test_try_usizei64, usize, i64 }
+ }
+
+ #[cfg(target_pointer_width = "32")] {
+ test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
+ test_impl_try_from_always_ok! { test_try_usizeu32, usize, u32 }
+ test_impl_try_from_always_ok! { test_try_isizei32, isize, i32 }
+ test_impl_try_from_always_ok! { test_try_u32usize, u32, usize }
+ test_impl_try_from_always_ok! { test_try_i32isize, i32, isize }
+ test_impl_try_from_always_ok! { test_try_usizei64, usize, i64 }
+ }
+
+ #[cfg(target_pointer_width = "64")] {
+ test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
+ test_impl_try_from_always_ok! { test_try_u32usize, u32, usize }
+ test_impl_try_from_always_ok! { test_try_u32isize, u32, isize }
+ test_impl_try_from_always_ok! { test_try_i32isize, i32, isize }
+ test_impl_try_from_always_ok! { test_try_u64usize, u64, usize }
+ test_impl_try_from_always_ok! { test_try_i64isize, i64, isize }
+ }
+ );
+}
+
+/// Conversions where max of $source can be represented as $target,
+macro_rules! test_impl_try_from_signed_to_unsigned_upper_ok {
+ ($fn_name:ident, $source:ty, $target:ty) => {
+ #[test]
+ fn $fn_name() {
+ let max = <$source>::MAX;
+ let min = <$source>::MIN;
+ let zero: $source = 0;
+ let neg_one: $source = -1;
+ assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target);
+ assert!(<$target as TryFrom<$source>>::try_from(min).is_err());
+ assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target);
+ assert!(<$target as TryFrom<$source>>::try_from(neg_one).is_err());
+ }
+ };
+}
+
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u8, i8, u8 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u16, i8, u16 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u32, i8, u32 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u64, i8, u64 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u128, i8, u128 }
+
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u16, i16, u16 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u32, i16, u32 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u64, i16, u64 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u128, i16, u128 }
+
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32u32, i32, u32 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32u64, i32, u64 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32u128, i32, u128 }
+
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i64u64, i64, u64 }
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i64u128, i64, u128 }
+
+test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i128u128, i128, u128 }
+
+assume_usize_width! {
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8usize, i8, usize }
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16usize, i16, usize }
+
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu64, isize, u64 }
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu128, isize, u128 }
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeusize, isize, usize }
+
+ cfg_block!(
+ #[cfg(target_pointer_width = "16")] {
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu16, isize, u16 }
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu32, isize, u32 }
+ }
+
+ #[cfg(target_pointer_width = "32")] {
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu32, isize, u32 }
+
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32usize, i32, usize }
+ }
+
+ #[cfg(target_pointer_width = "64")] {
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32usize, i32, usize }
+ test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i64usize, i64, usize }
+ }
+ );
+}
+
+/// Conversions where max of $source can not be represented as $target,
+/// but min can.
+macro_rules! test_impl_try_from_unsigned_to_signed_upper_err {
+ ($fn_name:ident, $source:ty, $target:ty) => {
+ #[test]
+ fn $fn_name() {
+ let max = <$source>::MAX;
+ let min = <$source>::MIN;
+ let zero: $source = 0;
+ assert!(<$target as TryFrom<$source>>::try_from(max).is_err());
+ assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target);
+ assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target);
+ }
+ };
+}
+
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u8i8, u8, i8 }
+
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16i8, u16, i8 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16i16, u16, i16 }
+
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i8, u32, i8 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i16, u32, i16 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i32, u32, i32 }
+
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i8, u64, i8 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i16, u64, i16 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i32, u64, i32 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i64, u64, i64 }
+
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i8, u128, i8 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i16, u128, i16 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i32, u128, i32 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i64, u128, i64 }
+test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i128, u128, i128 }
+
+assume_usize_width! {
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64isize, u64, isize }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128isize, u128, isize }
+
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei8, usize, i8 }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei16, usize, i16 }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizeisize, usize, isize }
+
+ cfg_block!(
+ #[cfg(target_pointer_width = "16")] {
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16isize, u16, isize }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32isize, u32, isize }
+ }
+
+ #[cfg(target_pointer_width = "32")] {
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32isize, u32, isize }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei32, usize, i32 }
+ }
+
+ #[cfg(target_pointer_width = "64")] {
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei32, usize, i32 }
+ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei64, usize, i64 }
+ }
+ );
+}
+
+/// Conversions where min/max of $source can not be represented as $target.
+macro_rules! test_impl_try_from_same_sign_err {
+ ($fn_name:ident, $source:ty, $target:ty) => {
+ #[test]
+ fn $fn_name() {
+ let max = <$source>::MAX;
+ let min = <$source>::MIN;
+ let zero: $source = 0;
+ let t_max = <$target>::MAX;
+ let t_min = <$target>::MIN;
+ assert!(<$target as TryFrom<$source>>::try_from(max).is_err());
+ if min != 0 {
+ assert!(<$target as TryFrom<$source>>::try_from(min).is_err());
+ }
+ assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target);
+ assert_eq!(
+ <$target as TryFrom<$source>>::try_from(t_max as $source).unwrap(),
+ t_max as $target
+ );
+ assert_eq!(
+ <$target as TryFrom<$source>>::try_from(t_min as $source).unwrap(),
+ t_min as $target
+ );
+ }
+ };
+}
+
+test_impl_try_from_same_sign_err! { test_try_u16u8, u16, u8 }
+
+test_impl_try_from_same_sign_err! { test_try_u32u8, u32, u8 }
+test_impl_try_from_same_sign_err! { test_try_u32u16, u32, u16 }
+
+test_impl_try_from_same_sign_err! { test_try_u64u8, u64, u8 }
+test_impl_try_from_same_sign_err! { test_try_u64u16, u64, u16 }
+test_impl_try_from_same_sign_err! { test_try_u64u32, u64, u32 }
+
+test_impl_try_from_same_sign_err! { test_try_u128u8, u128, u8 }
+test_impl_try_from_same_sign_err! { test_try_u128u16, u128, u16 }
+test_impl_try_from_same_sign_err! { test_try_u128u32, u128, u32 }
+test_impl_try_from_same_sign_err! { test_try_u128u64, u128, u64 }
+
+test_impl_try_from_same_sign_err! { test_try_i16i8, i16, i8 }
+test_impl_try_from_same_sign_err! { test_try_isizei8, isize, i8 }
+
+test_impl_try_from_same_sign_err! { test_try_i32i8, i32, i8 }
+test_impl_try_from_same_sign_err! { test_try_i32i16, i32, i16 }
+
+test_impl_try_from_same_sign_err! { test_try_i64i8, i64, i8 }
+test_impl_try_from_same_sign_err! { test_try_i64i16, i64, i16 }
+test_impl_try_from_same_sign_err! { test_try_i64i32, i64, i32 }
+
+test_impl_try_from_same_sign_err! { test_try_i128i8, i128, i8 }
+test_impl_try_from_same_sign_err! { test_try_i128i16, i128, i16 }
+test_impl_try_from_same_sign_err! { test_try_i128i32, i128, i32 }
+test_impl_try_from_same_sign_err! { test_try_i128i64, i128, i64 }
+
+assume_usize_width! {
+ test_impl_try_from_same_sign_err! { test_try_usizeu8, usize, u8 }
+ test_impl_try_from_same_sign_err! { test_try_u128usize, u128, usize }
+ test_impl_try_from_same_sign_err! { test_try_i128isize, i128, isize }
+
+ cfg_block!(
+ #[cfg(target_pointer_width = "16")] {
+ test_impl_try_from_same_sign_err! { test_try_u32usize, u32, usize }
+ test_impl_try_from_same_sign_err! { test_try_u64usize, u64, usize }
+
+ test_impl_try_from_same_sign_err! { test_try_i32isize, i32, isize }
+ test_impl_try_from_same_sign_err! { test_try_i64isize, i64, isize }
+ }
+
+ #[cfg(target_pointer_width = "32")] {
+ test_impl_try_from_same_sign_err! { test_try_u64usize, u64, usize }
+ test_impl_try_from_same_sign_err! { test_try_usizeu16, usize, u16 }
+
+ test_impl_try_from_same_sign_err! { test_try_i64isize, i64, isize }
+ test_impl_try_from_same_sign_err! { test_try_isizei16, isize, i16 }
+ }
+
+ #[cfg(target_pointer_width = "64")] {
+ test_impl_try_from_same_sign_err! { test_try_usizeu16, usize, u16 }
+ test_impl_try_from_same_sign_err! { test_try_usizeu32, usize, u32 }
+
+ test_impl_try_from_same_sign_err! { test_try_isizei16, isize, i16 }
+ test_impl_try_from_same_sign_err! { test_try_isizei32, isize, i32 }
+ }
+ );
+}
+
+/// Conversions where neither the min nor the max of $source can be represented by
+/// $target, but max/min of the target can be represented by the source.
+macro_rules! test_impl_try_from_signed_to_unsigned_err {
+ ($fn_name:ident, $source:ty, $target:ty) => {
+ #[test]
+ fn $fn_name() {
+ let max = <$source>::MAX;
+ let min = <$source>::MIN;
+ let zero: $source = 0;
+ let t_max = <$target>::MAX;
+ let t_min = <$target>::MIN;
+ assert!(<$target as TryFrom<$source>>::try_from(max).is_err());
+ assert!(<$target as TryFrom<$source>>::try_from(min).is_err());
+ assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target);
+ assert_eq!(
+ <$target as TryFrom<$source>>::try_from(t_max as $source).unwrap(),
+ t_max as $target
+ );
+ assert_eq!(
+ <$target as TryFrom<$source>>::try_from(t_min as $source).unwrap(),
+ t_min as $target
+ );
+ }
+ };
+}
+
+test_impl_try_from_signed_to_unsigned_err! { test_try_i16u8, i16, u8 }
+
+test_impl_try_from_signed_to_unsigned_err! { test_try_i32u8, i32, u8 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i32u16, i32, u16 }
+
+test_impl_try_from_signed_to_unsigned_err! { test_try_i64u8, i64, u8 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i64u16, i64, u16 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i64u32, i64, u32 }
+
+test_impl_try_from_signed_to_unsigned_err! { test_try_i128u8, i128, u8 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i128u16, i128, u16 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i128u32, i128, u32 }
+test_impl_try_from_signed_to_unsigned_err! { test_try_i128u64, i128, u64 }
+
+assume_usize_width! {
+ test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu8, isize, u8 }
+ test_impl_try_from_signed_to_unsigned_err! { test_try_i128usize, i128, usize }
+
+ cfg_block! {
+ #[cfg(target_pointer_width = "16")] {
+ test_impl_try_from_signed_to_unsigned_err! { test_try_i32usize, i32, usize }
+ test_impl_try_from_signed_to_unsigned_err! { test_try_i64usize, i64, usize }
+ }
+ #[cfg(target_pointer_width = "32")] {
+ test_impl_try_from_signed_to_unsigned_err! { test_try_i64usize, i64, usize }
+
+ test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu16, isize, u16 }
+ }
+ #[cfg(target_pointer_width = "64")] {
+ test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu16, isize, u16 }
+ test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu32, isize, u32 }
+ }
+ }
+}
+
+macro_rules! test_float {
+ ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr) => {
+ mod $modname {
+ #[test]
+ fn min() {
+ assert_eq!((0.0 as $fty).min(0.0), 0.0);
+ assert!((0.0 as $fty).min(0.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).min(-0.0), -0.0);
+ assert!((-0.0 as $fty).min(-0.0).is_sign_negative());
+ assert_eq!((9.0 as $fty).min(9.0), 9.0);
+ assert_eq!((-9.0 as $fty).min(0.0), -9.0);
+ assert_eq!((0.0 as $fty).min(9.0), 0.0);
+ assert!((0.0 as $fty).min(9.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).min(9.0), -0.0);
+ assert!((-0.0 as $fty).min(9.0).is_sign_negative());
+ assert_eq!((-0.0 as $fty).min(-9.0), -9.0);
+ assert_eq!(($inf as $fty).min(9.0), 9.0);
+ assert_eq!((9.0 as $fty).min($inf), 9.0);
+ assert_eq!(($inf as $fty).min(-9.0), -9.0);
+ assert_eq!((-9.0 as $fty).min($inf), -9.0);
+ assert_eq!(($neginf as $fty).min(9.0), $neginf);
+ assert_eq!((9.0 as $fty).min($neginf), $neginf);
+ assert_eq!(($neginf as $fty).min(-9.0), $neginf);
+ assert_eq!((-9.0 as $fty).min($neginf), $neginf);
+ assert_eq!(($nan as $fty).min(9.0), 9.0);
+ assert_eq!(($nan as $fty).min(-9.0), -9.0);
+ assert_eq!((9.0 as $fty).min($nan), 9.0);
+ assert_eq!((-9.0 as $fty).min($nan), -9.0);
+ assert!(($nan as $fty).min($nan).is_nan());
+ }
+ #[test]
+ fn max() {
+ assert_eq!((0.0 as $fty).max(0.0), 0.0);
+ assert!((0.0 as $fty).max(0.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).max(-0.0), -0.0);
+ assert!((-0.0 as $fty).max(-0.0).is_sign_negative());
+ assert_eq!((9.0 as $fty).max(9.0), 9.0);
+ assert_eq!((-9.0 as $fty).max(0.0), 0.0);
+ assert!((-9.0 as $fty).max(0.0).is_sign_positive());
+ assert_eq!((-9.0 as $fty).max(-0.0), -0.0);
+ assert!((-9.0 as $fty).max(-0.0).is_sign_negative());
+ assert_eq!((0.0 as $fty).max(9.0), 9.0);
+ assert_eq!((0.0 as $fty).max(-9.0), 0.0);
+ assert!((0.0 as $fty).max(-9.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).max(-9.0), -0.0);
+ assert!((-0.0 as $fty).max(-9.0).is_sign_negative());
+ assert_eq!(($inf as $fty).max(9.0), $inf);
+ assert_eq!((9.0 as $fty).max($inf), $inf);
+ assert_eq!(($inf as $fty).max(-9.0), $inf);
+ assert_eq!((-9.0 as $fty).max($inf), $inf);
+ assert_eq!(($neginf as $fty).max(9.0), 9.0);
+ assert_eq!((9.0 as $fty).max($neginf), 9.0);
+ assert_eq!(($neginf as $fty).max(-9.0), -9.0);
+ assert_eq!((-9.0 as $fty).max($neginf), -9.0);
+ assert_eq!(($nan as $fty).max(9.0), 9.0);
+ assert_eq!(($nan as $fty).max(-9.0), -9.0);
+ assert_eq!((9.0 as $fty).max($nan), 9.0);
+ assert_eq!((-9.0 as $fty).max($nan), -9.0);
+ assert!(($nan as $fty).max($nan).is_nan());
+ }
+ #[test]
+ fn minimum() {
+ assert_eq!((0.0 as $fty).minimum(0.0), 0.0);
+ assert!((0.0 as $fty).minimum(0.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).minimum(0.0), -0.0);
+ assert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
+ assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0);
+ assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
+ assert_eq!((9.0 as $fty).minimum(9.0), 9.0);
+ assert_eq!((-9.0 as $fty).minimum(0.0), -9.0);
+ assert_eq!((0.0 as $fty).minimum(9.0), 0.0);
+ assert!((0.0 as $fty).minimum(9.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).minimum(9.0), -0.0);
+ assert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
+ assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0);
+ assert_eq!(($inf as $fty).minimum(9.0), 9.0);
+ assert_eq!((9.0 as $fty).minimum($inf), 9.0);
+ assert_eq!(($inf as $fty).minimum(-9.0), -9.0);
+ assert_eq!((-9.0 as $fty).minimum($inf), -9.0);
+ assert_eq!(($neginf as $fty).minimum(9.0), $neginf);
+ assert_eq!((9.0 as $fty).minimum($neginf), $neginf);
+ assert_eq!(($neginf as $fty).minimum(-9.0), $neginf);
+ assert_eq!((-9.0 as $fty).minimum($neginf), $neginf);
+ assert!(($nan as $fty).minimum(9.0).is_nan());
+ assert!(($nan as $fty).minimum(-9.0).is_nan());
+ assert!((9.0 as $fty).minimum($nan).is_nan());
+ assert!((-9.0 as $fty).minimum($nan).is_nan());
+ assert!(($nan as $fty).minimum($nan).is_nan());
+ }
+ #[test]
+ fn maximum() {
+ assert_eq!((0.0 as $fty).maximum(0.0), 0.0);
+ assert!((0.0 as $fty).maximum(0.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).maximum(0.0), 0.0);
+ assert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0);
+ assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
+ assert_eq!((9.0 as $fty).maximum(9.0), 9.0);
+ assert_eq!((-9.0 as $fty).maximum(0.0), 0.0);
+ assert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
+ assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0);
+ assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
+ assert_eq!((0.0 as $fty).maximum(9.0), 9.0);
+ assert_eq!((0.0 as $fty).maximum(-9.0), 0.0);
+ assert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
+ assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0);
+ assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
+ assert_eq!(($inf as $fty).maximum(9.0), $inf);
+ assert_eq!((9.0 as $fty).maximum($inf), $inf);
+ assert_eq!(($inf as $fty).maximum(-9.0), $inf);
+ assert_eq!((-9.0 as $fty).maximum($inf), $inf);
+ assert_eq!(($neginf as $fty).maximum(9.0), 9.0);
+ assert_eq!((9.0 as $fty).maximum($neginf), 9.0);
+ assert_eq!(($neginf as $fty).maximum(-9.0), -9.0);
+ assert_eq!((-9.0 as $fty).maximum($neginf), -9.0);
+ assert!(($nan as $fty).maximum(9.0).is_nan());
+ assert!(($nan as $fty).maximum(-9.0).is_nan());
+ assert!((9.0 as $fty).maximum($nan).is_nan());
+ assert!((-9.0 as $fty).maximum($nan).is_nan());
+ assert!(($nan as $fty).maximum($nan).is_nan());
+ }
+ #[test]
+ fn rem_euclid() {
+ let a: $fty = 42.0;
+ assert!($inf.rem_euclid(a).is_nan());
+ assert_eq!(a.rem_euclid($inf), a);
+ assert!(a.rem_euclid($nan).is_nan());
+ assert!($inf.rem_euclid($inf).is_nan());
+ assert!($inf.rem_euclid($nan).is_nan());
+ assert!($nan.rem_euclid($inf).is_nan());
+ }
+ #[test]
+ fn div_euclid() {
+ let a: $fty = 42.0;
+ assert_eq!(a.div_euclid($inf), 0.0);
+ assert!(a.div_euclid($nan).is_nan());
+ assert!($inf.div_euclid($inf).is_nan());
+ assert!($inf.div_euclid($nan).is_nan());
+ assert!($nan.div_euclid($inf).is_nan());
+ }
+ }
+ };
+}
+
+test_float!(f32, f32, f32::INFINITY, f32::NEG_INFINITY, f32::NAN);
+test_float!(f64, f64, f64::INFINITY, f64::NEG_INFINITY, f64::NAN);
diff --git a/library/core/tests/num/nan.rs b/library/core/tests/num/nan.rs
new file mode 100644
index 000000000..ef81988c9
--- /dev/null
+++ b/library/core/tests/num/nan.rs
@@ -0,0 +1,7 @@
+#[test]
+fn test_nan() {
+ let x = "NaN".to_string();
+ assert_eq!(format!("{}", f64::NAN), x);
+ assert_eq!(format!("{:e}", f64::NAN), x);
+ assert_eq!(format!("{:E}", f64::NAN), x);
+}
diff --git a/library/core/tests/num/ops.rs b/library/core/tests/num/ops.rs
new file mode 100644
index 000000000..ae8b93825
--- /dev/null
+++ b/library/core/tests/num/ops.rs
@@ -0,0 +1,232 @@
+use core::ops::*;
+
+// For types L and R, checks that a trait implementation exists for
+// * binary ops: L op R, L op &R, &L op R and &L op &R
+// * assign ops: &mut L op R, &mut L op &R
+macro_rules! impl_defined {
+ ($op:ident, $method:ident($lhs:literal, $rhs:literal), $result:literal, $lt:ty, $rt:ty) => {
+ let lhs = $lhs as $lt;
+ let rhs = $rhs as $rt;
+ assert_eq!($result as $lt, $op::$method(lhs, rhs));
+ assert_eq!($result as $lt, $op::$method(lhs, &rhs));
+ assert_eq!($result as $lt, $op::$method(&lhs, rhs));
+ assert_eq!($result as $lt, $op::$method(&lhs, &rhs));
+ };
+ ($op:ident, $method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $lt:ty, $rt:ty) => {
+ let rhs = $rhs as $rt;
+ let mut lhs = $lhs as $lt;
+ $op::$method(&mut lhs, rhs);
+ assert_eq!($result as $lt, lhs);
+
+ let mut lhs = $lhs as $lt;
+ $op::$method(&mut lhs, &rhs);
+ assert_eq!($result as $lt, lhs);
+ };
+}
+
+// For all specified types T, checks that a trait implementation exists for
+// * binary ops: T op T, T op &T, &T op T and &T op &T
+// * assign ops: &mut T op T, &mut T op &T
+// * unary ops: op T and op &T
+macro_rules! impls_defined {
+ ($op:ident, $method:ident($lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {$(
+ impl_defined!($op, $method($lhs, $rhs), $result, $t, $t);
+ )+};
+ ($op:ident, $method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {$(
+ impl_defined!($op, $method(&mut $lhs, $rhs), $result, $t, $t);
+ )+};
+ ($op:ident, $method:ident($operand:literal), $result:literal, $($t:ty),+) => {$(
+ let operand = $operand as $t;
+ assert_eq!($result as $t, $op::$method(operand));
+ assert_eq!($result as $t, $op::$method(&operand));
+ )+};
+}
+
+macro_rules! test_op {
+ ($fn_name:ident, $op:ident::$method:ident($lhs:literal), $result:literal, $($t:ty),+) => {
+ #[test]
+ fn $fn_name() {
+ impls_defined!($op, $method($lhs), $result, $($t),+);
+ }
+ };
+}
+
+test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64);
+#[cfg(not(target_os = "emscripten"))]
+test_op!(test_neg_defined_128, Neg::neg(0), 0, i128);
+
+test_op!(test_not_defined_bool, Not::not(true), false, bool);
+
+macro_rules! test_arith_op {
+ ($fn_name:ident, $op:ident::$method:ident($lhs:literal, $rhs:literal)) => {
+ #[test]
+ fn $fn_name() {
+ impls_defined!(
+ $op,
+ $method($lhs, $rhs),
+ 0,
+ i8,
+ i16,
+ i32,
+ i64,
+ isize,
+ u8,
+ u16,
+ u32,
+ u64,
+ usize,
+ f32,
+ f64
+ );
+ #[cfg(not(target_os = "emscripten"))]
+ impls_defined!($op, $method($lhs, $rhs), 0, i128, u128);
+ }
+ };
+ ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => {
+ #[test]
+ fn $fn_name() {
+ impls_defined!(
+ $op,
+ $method(&mut $lhs, $rhs),
+ 0,
+ i8,
+ i16,
+ i32,
+ i64,
+ isize,
+ u8,
+ u16,
+ u32,
+ u64,
+ usize,
+ f32,
+ f64
+ );
+ #[cfg(not(target_os = "emscripten"))]
+ impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128);
+ }
+ };
+}
+
+test_arith_op!(test_add_defined, Add::add(0, 0));
+test_arith_op!(test_add_assign_defined, AddAssign::add_assign(&mut 0, 0));
+test_arith_op!(test_sub_defined, Sub::sub(0, 0));
+test_arith_op!(test_sub_assign_defined, SubAssign::sub_assign(&mut 0, 0));
+test_arith_op!(test_mul_defined, Mul::mul(0, 0));
+test_arith_op!(test_mul_assign_defined, MulAssign::mul_assign(&mut 0, 0));
+test_arith_op!(test_div_defined, Div::div(0, 1));
+test_arith_op!(test_div_assign_defined, DivAssign::div_assign(&mut 0, 1));
+test_arith_op!(test_rem_defined, Rem::rem(0, 1));
+test_arith_op!(test_rem_assign_defined, RemAssign::rem_assign(&mut 0, 1));
+
+macro_rules! test_bitop {
+ ($test_name:ident, $op:ident::$method:ident) => {
+ #[test]
+ fn $test_name() {
+ impls_defined!(
+ $op,
+ $method(0, 0),
+ 0,
+ i8,
+ i16,
+ i32,
+ i64,
+ isize,
+ u8,
+ u16,
+ u32,
+ u64,
+ usize
+ );
+ #[cfg(not(target_os = "emscripten"))]
+ impls_defined!($op, $method(0, 0), 0, i128, u128);
+ impls_defined!($op, $method(false, false), false, bool);
+ }
+ };
+}
+macro_rules! test_bitop_assign {
+ ($test_name:ident, $op:ident::$method:ident) => {
+ #[test]
+ fn $test_name() {
+ impls_defined!(
+ $op,
+ $method(&mut 0, 0),
+ 0,
+ i8,
+ i16,
+ i32,
+ i64,
+ isize,
+ u8,
+ u16,
+ u32,
+ u64,
+ usize
+ );
+ #[cfg(not(target_os = "emscripten"))]
+ impls_defined!($op, $method(&mut 0, 0), 0, i128, u128);
+ impls_defined!($op, $method(&mut false, false), false, bool);
+ }
+ };
+}
+
+test_bitop!(test_bitand_defined, BitAnd::bitand);
+test_bitop_assign!(test_bitand_assign_defined, BitAndAssign::bitand_assign);
+test_bitop!(test_bitor_defined, BitOr::bitor);
+test_bitop_assign!(test_bitor_assign_defined, BitOrAssign::bitor_assign);
+test_bitop!(test_bitxor_defined, BitXor::bitxor);
+test_bitop_assign!(test_bitxor_assign_defined, BitXorAssign::bitxor_assign);
+
+macro_rules! test_shift_inner {
+ ($op:ident::$method:ident, $lt:ty, $($rt:ty),+) => {
+ $(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+
+ };
+ ($op:ident::$method:ident, $lt:ty) => {
+ test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
+ #[cfg(not(target_os = "emscripten"))]
+ test_shift_inner!($op::$method, $lt, i128, u128);
+ };
+}
+
+macro_rules! test_shift {
+ ($op:ident::$method:ident, $($lt:ty),+) => {
+ $(test_shift_inner!($op::$method, $lt);)+
+ };
+ ($test_name:ident, $op:ident::$method:ident) => {
+ #[test]
+ fn $test_name() {
+ test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
+ #[cfg(not(target_os = "emscripten"))]
+ test_shift!($op::$method, i128, u128);
+ }
+ };
+}
+
+macro_rules! test_shift_assign_inner {
+ ($op:ident::$method:ident, $lt:ty, $($rt:ty),+) => {
+ $(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+
+ };
+ ($op:ident::$method:ident, $lt:ty) => {
+ test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
+ #[cfg(not(target_os = "emscripten"))]
+ test_shift_assign_inner!($op::$method, $lt, i128, u128);
+ };
+}
+
+macro_rules! test_shift_assign {
+ ($op:ident::$method:ident, $($lt:ty),+) => {
+ $(test_shift_assign_inner!($op::$method, $lt);)+
+ };
+ ($test_name:ident, $op:ident::$method:ident) => {
+ #[test]
+ fn $test_name() {
+ test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
+ #[cfg(not(target_os = "emscripten"))]
+ test_shift_assign!($op::$method, i128, u128);
+ }
+ };
+}
+test_shift!(test_shl_defined, Shl::shl);
+test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign);
+test_shift!(test_shr_defined, Shr::shr);
+test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign);
diff --git a/library/core/tests/num/u128.rs b/library/core/tests/num/u128.rs
new file mode 100644
index 000000000..a7b0f9eff
--- /dev/null
+++ b/library/core/tests/num/u128.rs
@@ -0,0 +1 @@
+uint_module!(u128);
diff --git a/library/core/tests/num/u16.rs b/library/core/tests/num/u16.rs
new file mode 100644
index 000000000..010596a34
--- /dev/null
+++ b/library/core/tests/num/u16.rs
@@ -0,0 +1 @@
+uint_module!(u16);
diff --git a/library/core/tests/num/u32.rs b/library/core/tests/num/u32.rs
new file mode 100644
index 000000000..687d3bbaa
--- /dev/null
+++ b/library/core/tests/num/u32.rs
@@ -0,0 +1 @@
+uint_module!(u32);
diff --git a/library/core/tests/num/u64.rs b/library/core/tests/num/u64.rs
new file mode 100644
index 000000000..ee55071e9
--- /dev/null
+++ b/library/core/tests/num/u64.rs
@@ -0,0 +1 @@
+uint_module!(u64);
diff --git a/library/core/tests/num/u8.rs b/library/core/tests/num/u8.rs
new file mode 100644
index 000000000..12b038ce0
--- /dev/null
+++ b/library/core/tests/num/u8.rs
@@ -0,0 +1 @@
+uint_module!(u8);
diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs
new file mode 100644
index 000000000..93ae620c2
--- /dev/null
+++ b/library/core/tests/num/uint_macros.rs
@@ -0,0 +1,235 @@
+macro_rules! uint_module {
+ ($T:ident) => {
+ #[cfg(test)]
+ mod tests {
+ use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
+ use core::$T::*;
+ use std::str::FromStr;
+
+ use crate::num;
+
+ #[test]
+ fn test_overflows() {
+ assert!(MAX > 0);
+ assert!(MIN <= 0);
+ assert!((MIN + MAX).wrapping_add(1) == 0);
+ }
+
+ #[test]
+ fn test_num() {
+ num::test_num(10 as $T, 2 as $T);
+ }
+
+ #[test]
+ fn test_bitwise_operators() {
+ assert!(0b1110 as $T == (0b1100 as $T).bitor(0b1010 as $T));
+ assert!(0b1000 as $T == (0b1100 as $T).bitand(0b1010 as $T));
+ assert!(0b0110 as $T == (0b1100 as $T).bitxor(0b1010 as $T));
+ assert!(0b1110 as $T == (0b0111 as $T).shl(1));
+ assert!(0b0111 as $T == (0b1110 as $T).shr(1));
+ assert!(MAX - (0b1011 as $T) == (0b1011 as $T).not());
+ }
+
+ const A: $T = 0b0101100;
+ const B: $T = 0b0100001;
+ const C: $T = 0b1111001;
+
+ const _0: $T = 0;
+ const _1: $T = !0;
+
+ #[test]
+ fn test_count_ones() {
+ assert!(A.count_ones() == 3);
+ assert!(B.count_ones() == 2);
+ assert!(C.count_ones() == 5);
+ }
+
+ #[test]
+ fn test_count_zeros() {
+ assert!(A.count_zeros() == $T::BITS - 3);
+ assert!(B.count_zeros() == $T::BITS - 2);
+ assert!(C.count_zeros() == $T::BITS - 5);
+ }
+
+ #[test]
+ fn test_leading_trailing_ones() {
+ let a: $T = 0b0101_1111;
+ assert_eq!(a.trailing_ones(), 5);
+ assert_eq!((!a).leading_ones(), $T::BITS - 7);
+
+ assert_eq!(a.reverse_bits().leading_ones(), 5);
+
+ assert_eq!(_1.leading_ones(), $T::BITS);
+ assert_eq!(_1.trailing_ones(), $T::BITS);
+
+ assert_eq!((_1 << 1).trailing_ones(), 0);
+ assert_eq!((_1 >> 1).leading_ones(), 0);
+
+ assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
+ assert_eq!((_1 >> 1).trailing_ones(), $T::BITS - 1);
+
+ assert_eq!(_0.leading_ones(), 0);
+ assert_eq!(_0.trailing_ones(), 0);
+
+ let x: $T = 0b0010_1100;
+ assert_eq!(x.leading_ones(), 0);
+ assert_eq!(x.trailing_ones(), 0);
+ }
+
+ #[test]
+ fn test_rotate() {
+ assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
+ assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
+ assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
+
+ // Rotating these should make no difference
+ //
+ // We test using 124 bits because to ensure that overlong bit shifts do
+ // not cause undefined behaviour. See #10183.
+ assert_eq!(_0.rotate_left(124), _0);
+ assert_eq!(_1.rotate_left(124), _1);
+ assert_eq!(_0.rotate_right(124), _0);
+ assert_eq!(_1.rotate_right(124), _1);
+
+ // Rotating by 0 should have no effect
+ assert_eq!(A.rotate_left(0), A);
+ assert_eq!(B.rotate_left(0), B);
+ assert_eq!(C.rotate_left(0), C);
+ // Rotating by a multiple of word size should also have no effect
+ assert_eq!(A.rotate_left(128), A);
+ assert_eq!(B.rotate_left(128), B);
+ assert_eq!(C.rotate_left(128), C);
+ }
+
+ #[test]
+ fn test_swap_bytes() {
+ assert_eq!(A.swap_bytes().swap_bytes(), A);
+ assert_eq!(B.swap_bytes().swap_bytes(), B);
+ assert_eq!(C.swap_bytes().swap_bytes(), C);
+
+ // Swapping these should make no difference
+ assert_eq!(_0.swap_bytes(), _0);
+ assert_eq!(_1.swap_bytes(), _1);
+ }
+
+ #[test]
+ fn test_reverse_bits() {
+ assert_eq!(A.reverse_bits().reverse_bits(), A);
+ assert_eq!(B.reverse_bits().reverse_bits(), B);
+ assert_eq!(C.reverse_bits().reverse_bits(), C);
+
+ // Swapping these should make no difference
+ assert_eq!(_0.reverse_bits(), _0);
+ assert_eq!(_1.reverse_bits(), _1);
+ }
+
+ #[test]
+ fn test_le() {
+ assert_eq!($T::from_le(A.to_le()), A);
+ assert_eq!($T::from_le(B.to_le()), B);
+ assert_eq!($T::from_le(C.to_le()), C);
+ assert_eq!($T::from_le(_0), _0);
+ assert_eq!($T::from_le(_1), _1);
+ assert_eq!(_0.to_le(), _0);
+ assert_eq!(_1.to_le(), _1);
+ }
+
+ #[test]
+ fn test_be() {
+ assert_eq!($T::from_be(A.to_be()), A);
+ assert_eq!($T::from_be(B.to_be()), B);
+ assert_eq!($T::from_be(C.to_be()), C);
+ assert_eq!($T::from_be(_0), _0);
+ assert_eq!($T::from_be(_1), _1);
+ assert_eq!(_0.to_be(), _0);
+ assert_eq!(_1.to_be(), _1);
+ }
+
+ #[test]
+ fn test_unsigned_checked_div() {
+ assert!((10 as $T).checked_div(2) == Some(5));
+ assert!((5 as $T).checked_div(0) == None);
+ }
+
+ fn from_str<T: FromStr>(t: &str) -> Option<T> {
+ FromStr::from_str(t).ok()
+ }
+
+ #[test]
+ pub fn test_from_str() {
+ assert_eq!(from_str::<$T>("0"), Some(0 as $T));
+ assert_eq!(from_str::<$T>("3"), Some(3 as $T));
+ assert_eq!(from_str::<$T>("10"), Some(10 as $T));
+ assert_eq!(from_str::<u32>("123456789"), Some(123456789 as u32));
+ assert_eq!(from_str::<$T>("00100"), Some(100 as $T));
+
+ assert_eq!(from_str::<$T>(""), None);
+ assert_eq!(from_str::<$T>(" "), None);
+ assert_eq!(from_str::<$T>("x"), None);
+ }
+
+ #[test]
+ pub fn test_parse_bytes() {
+ assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T));
+ assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T));
+ assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T));
+ assert_eq!(u16::from_str_radix("123", 16), Ok(291 as u16));
+ assert_eq!(u16::from_str_radix("ffff", 16), Ok(65535 as u16));
+ assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T));
+
+ assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>);
+ assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>);
+ }
+
+ #[test]
+ fn test_pow() {
+ let mut r = 2 as $T;
+ assert_eq!(r.pow(2), 4 as $T);
+ assert_eq!(r.pow(0), 1 as $T);
+ assert_eq!(r.wrapping_pow(2), 4 as $T);
+ assert_eq!(r.wrapping_pow(0), 1 as $T);
+ assert_eq!(r.checked_pow(2), Some(4 as $T));
+ assert_eq!(r.checked_pow(0), Some(1 as $T));
+ assert_eq!(r.overflowing_pow(2), (4 as $T, false));
+ assert_eq!(r.overflowing_pow(0), (1 as $T, false));
+ assert_eq!(r.saturating_pow(2), 4 as $T);
+ assert_eq!(r.saturating_pow(0), 1 as $T);
+
+ r = MAX;
+ // use `^` to represent .pow() with no overflow.
+ // if itest::MAX == 2^j-1, then itest is a `j` bit int,
+ // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
+ // thussaturating_pow the overflowing result is exactly 1.
+ assert_eq!(r.wrapping_pow(2), 1 as $T);
+ assert_eq!(r.checked_pow(2), None);
+ assert_eq!(r.overflowing_pow(2), (1 as $T, true));
+ assert_eq!(r.saturating_pow(2), MAX);
+ }
+
+ #[test]
+ fn test_div_floor() {
+ assert_eq!((8 as $T).div_floor(3), 2);
+ }
+
+ #[test]
+ fn test_div_ceil() {
+ assert_eq!((8 as $T).div_ceil(3), 3);
+ }
+
+ #[test]
+ fn test_next_multiple_of() {
+ assert_eq!((16 as $T).next_multiple_of(8), 16);
+ assert_eq!((23 as $T).next_multiple_of(8), 24);
+ assert_eq!(MAX.next_multiple_of(1), MAX);
+ }
+
+ #[test]
+ fn test_checked_next_multiple_of() {
+ assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16));
+ assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24));
+ assert_eq!((1 as $T).checked_next_multiple_of(0), None);
+ assert_eq!(MAX.checked_next_multiple_of(2), None);
+ }
+ }
+ };
+}
diff --git a/library/core/tests/num/wrapping.rs b/library/core/tests/num/wrapping.rs
new file mode 100644
index 000000000..8ded139a1
--- /dev/null
+++ b/library/core/tests/num/wrapping.rs
@@ -0,0 +1,320 @@
+use core::num::Wrapping;
+
+macro_rules! wrapping_operation {
+ ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
+ assert_eq!($result, $lhs $op $rhs);
+ assert_eq!($result, &$lhs $op $rhs);
+ assert_eq!($result, $lhs $op &$rhs);
+ assert_eq!($result, &$lhs $op &$rhs);
+ };
+ ($result:expr, $op:tt $expr:expr) => {
+ assert_eq!($result, $op $expr);
+ assert_eq!($result, $op &$expr);
+ };
+}
+
+macro_rules! wrapping_assignment {
+ ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
+ let mut lhs1 = $lhs;
+ lhs1 $op $rhs;
+ assert_eq!($result, lhs1);
+
+ let mut lhs2 = $lhs;
+ lhs2 $op &$rhs;
+ assert_eq!($result, lhs2);
+ };
+}
+
+macro_rules! wrapping_test {
+ ($fn_name:ident, $type:ty, $min:expr, $max:expr) => {
+ #[test]
+ fn $fn_name() {
+ let zero: Wrapping<$type> = Wrapping(0);
+ let one: Wrapping<$type> = Wrapping(1);
+ let min: Wrapping<$type> = Wrapping($min);
+ let max: Wrapping<$type> = Wrapping($max);
+
+ wrapping_operation!(min, max + one);
+ wrapping_assignment!(min, max += one);
+ wrapping_operation!(max, min - one);
+ wrapping_assignment!(max, min -= one);
+ wrapping_operation!(max, max * one);
+ wrapping_assignment!(max, max *= one);
+ wrapping_operation!(max, max / one);
+ wrapping_assignment!(max, max /= one);
+ wrapping_operation!(zero, max % one);
+ wrapping_assignment!(zero, max %= one);
+ wrapping_operation!(zero, zero & max);
+ wrapping_assignment!(zero, zero &= max);
+ wrapping_operation!(max, zero | max);
+ wrapping_assignment!(max, zero |= max);
+ wrapping_operation!(zero, max ^ max);
+ wrapping_assignment!(zero, max ^= max);
+ wrapping_operation!(zero, zero << 1usize);
+ wrapping_assignment!(zero, zero <<= 1usize);
+ wrapping_operation!(zero, zero >> 1usize);
+ wrapping_assignment!(zero, zero >>= 1usize);
+ wrapping_operation!(zero, -zero);
+ wrapping_operation!(max, !min);
+ }
+ };
+}
+
+wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX);
+wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX);
+wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX);
+wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX);
+#[cfg(not(target_os = "emscripten"))]
+wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX);
+wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX);
+wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX);
+wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX);
+wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX);
+wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX);
+#[cfg(not(target_os = "emscripten"))]
+wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX);
+wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX);
+
+// Don't warn about overflowing ops on 32-bit platforms
+#[cfg_attr(target_pointer_width = "32", allow(const_err))]
+#[test]
+fn wrapping_int_api() {
+ assert_eq!(i8::MAX.wrapping_add(1), i8::MIN);
+ assert_eq!(i16::MAX.wrapping_add(1), i16::MIN);
+ assert_eq!(i32::MAX.wrapping_add(1), i32::MIN);
+ assert_eq!(i64::MAX.wrapping_add(1), i64::MIN);
+ assert_eq!(isize::MAX.wrapping_add(1), isize::MIN);
+
+ assert_eq!(i8::MIN.wrapping_sub(1), i8::MAX);
+ assert_eq!(i16::MIN.wrapping_sub(1), i16::MAX);
+ assert_eq!(i32::MIN.wrapping_sub(1), i32::MAX);
+ assert_eq!(i64::MIN.wrapping_sub(1), i64::MAX);
+ assert_eq!(isize::MIN.wrapping_sub(1), isize::MAX);
+
+ assert_eq!(u8::MAX.wrapping_add(1), u8::MIN);
+ assert_eq!(u16::MAX.wrapping_add(1), u16::MIN);
+ assert_eq!(u32::MAX.wrapping_add(1), u32::MIN);
+ assert_eq!(u64::MAX.wrapping_add(1), u64::MIN);
+ assert_eq!(usize::MAX.wrapping_add(1), usize::MIN);
+
+ assert_eq!(u8::MIN.wrapping_sub(1), u8::MAX);
+ assert_eq!(u16::MIN.wrapping_sub(1), u16::MAX);
+ assert_eq!(u32::MIN.wrapping_sub(1), u32::MAX);
+ assert_eq!(u64::MIN.wrapping_sub(1), u64::MAX);
+ assert_eq!(usize::MIN.wrapping_sub(1), usize::MAX);
+
+ assert_eq!((0xfe_u8 as i8).wrapping_mul(16), (0xe0_u8 as i8));
+ assert_eq!((0xfedc_u16 as i16).wrapping_mul(16), (0xedc0_u16 as i16));
+ assert_eq!((0xfedc_ba98_u32 as i32).wrapping_mul(16), (0xedcb_a980_u32 as i32));
+ assert_eq!(
+ (0xfedc_ba98_7654_3217_u64 as i64).wrapping_mul(16),
+ (0xedcb_a987_6543_2170_u64 as i64)
+ );
+
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ assert_eq!((0xfedc_ba98_u32 as isize).wrapping_mul(16), (0xedcb_a980_u32 as isize));
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ assert_eq!(
+ (0xfedc_ba98_7654_3217_u64 as isize).wrapping_mul(16),
+ (0xedcb_a987_6543_2170_u64 as isize)
+ );
+ }
+ }
+
+ assert_eq!((0xfe as u8).wrapping_mul(16), (0xe0 as u8));
+ assert_eq!((0xfedc as u16).wrapping_mul(16), (0xedc0 as u16));
+ assert_eq!((0xfedc_ba98 as u32).wrapping_mul(16), (0xedcb_a980 as u32));
+ assert_eq!((0xfedc_ba98_7654_3217 as u64).wrapping_mul(16), (0xedcb_a987_6543_2170 as u64));
+
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ assert_eq!((0xfedc_ba98 as usize).wrapping_mul(16), (0xedcb_a980 as usize));
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ assert_eq!(
+ (0xfedc_ba98_7654_3217 as usize).wrapping_mul(16),
+ (0xedcb_a987_6543_2170 as usize)
+ );
+ }
+ }
+
+ macro_rules! check_mul_no_wrap {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_mul($f), ($e) * $f);
+ };
+ }
+ macro_rules! check_mul_wraps {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_mul($f), $e);
+ };
+ }
+
+ check_mul_no_wrap!(0xfe_u8 as i8, -1);
+ check_mul_no_wrap!(0xfedc_u16 as i16, -1);
+ check_mul_no_wrap!(0xfedc_ba98_u32 as i32, -1);
+ check_mul_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -1);
+ check_mul_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, -1);
+
+ check_mul_no_wrap!(0xfe_u8 as i8, -2);
+ check_mul_no_wrap!(0xfedc_u16 as i16, -2);
+ check_mul_no_wrap!(0xfedc_ba98_u32 as i32, -2);
+ check_mul_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -2);
+ check_mul_no_wrap!(0xfedc_ba98_fedc_ba98_u64 as u64 as isize, -2);
+
+ check_mul_no_wrap!(0xfe_u8 as i8, 2);
+ check_mul_no_wrap!(0xfedc_u16 as i16, 2);
+ check_mul_no_wrap!(0xfedc_ba98_u32 as i32, 2);
+ check_mul_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, 2);
+ check_mul_no_wrap!(0xfedc_ba98_fedc_ba98_u64 as u64 as isize, 2);
+
+ check_mul_wraps!(0x80_u8 as i8, -1);
+ check_mul_wraps!(0x8000_u16 as i16, -1);
+ check_mul_wraps!(0x8000_0000_u32 as i32, -1);
+ check_mul_wraps!(0x8000_0000_0000_0000_u64 as i64, -1);
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ check_mul_wraps!(0x8000_0000_u32 as isize, -1);
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ check_mul_wraps!(0x8000_0000_0000_0000_u64 as isize, -1);
+ }
+ }
+
+ macro_rules! check_div_no_wrap {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_div($f), ($e) / $f);
+ };
+ }
+ macro_rules! check_div_wraps {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_div($f), $e);
+ };
+ }
+
+ check_div_no_wrap!(0xfe_u8 as i8, -1);
+ check_div_no_wrap!(0xfedc_u16 as i16, -1);
+ check_div_no_wrap!(0xfedc_ba98_u32 as i32, -1);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -1);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, -1);
+
+ check_div_no_wrap!(0xfe_u8 as i8, -2);
+ check_div_no_wrap!(0xfedc_u16 as i16, -2);
+ check_div_no_wrap!(0xfedc_ba98_u32 as i32, -2);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -2);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, -2);
+
+ check_div_no_wrap!(0xfe_u8 as i8, 2);
+ check_div_no_wrap!(0xfedc_u16 as i16, 2);
+ check_div_no_wrap!(0xfedc_ba98_u32 as i32, 2);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, 2);
+ check_div_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, 2);
+
+ check_div_wraps!(-128 as i8, -1);
+ check_div_wraps!(0x8000_u16 as i16, -1);
+ check_div_wraps!(0x8000_0000_u32 as i32, -1);
+ check_div_wraps!(0x8000_0000_0000_0000_u64 as i64, -1);
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ check_div_wraps!(0x8000_0000_u32 as isize, -1);
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ check_div_wraps!(0x8000_0000_0000_0000_u64 as isize, -1);
+ }
+ }
+
+ macro_rules! check_rem_no_wrap {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_rem($f), ($e) % $f);
+ };
+ }
+ macro_rules! check_rem_wraps {
+ ($e:expr, $f:expr) => {
+ assert_eq!(($e).wrapping_rem($f), 0);
+ };
+ }
+
+ check_rem_no_wrap!(0xfe_u8 as i8, -1);
+ check_rem_no_wrap!(0xfedc_u16 as i16, -1);
+ check_rem_no_wrap!(0xfedc_ba98_u32 as i32, -1);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -1);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, -1);
+
+ check_rem_no_wrap!(0xfe_u8 as i8, -2);
+ check_rem_no_wrap!(0xfedc_u16 as i16, -2);
+ check_rem_no_wrap!(0xfedc_ba98_u32 as i32, -2);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, -2);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, -2);
+
+ check_rem_no_wrap!(0xfe_u8 as i8, 2);
+ check_rem_no_wrap!(0xfedc_u16 as i16, 2);
+ check_rem_no_wrap!(0xfedc_ba98_u32 as i32, 2);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64, 2);
+ check_rem_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize, 2);
+
+ check_rem_wraps!(0x80_u8 as i8, -1);
+ check_rem_wraps!(0x8000_u16 as i16, -1);
+ check_rem_wraps!(0x8000_0000_u32 as i32, -1);
+ check_rem_wraps!(0x8000_0000_0000_0000_u64 as i64, -1);
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ check_rem_wraps!(0x8000_0000_u32 as isize, -1);
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ check_rem_wraps!(0x8000_0000_0000_0000_u64 as isize, -1);
+ }
+ }
+
+ macro_rules! check_neg_no_wrap {
+ ($e:expr) => {
+ assert_eq!(($e).wrapping_neg(), -($e));
+ };
+ }
+ macro_rules! check_neg_wraps {
+ ($e:expr) => {
+ assert_eq!(($e).wrapping_neg(), ($e));
+ };
+ }
+
+ check_neg_no_wrap!(0xfe_u8 as i8);
+ check_neg_no_wrap!(0xfedc_u16 as i16);
+ check_neg_no_wrap!(0xfedc_ba98_u32 as i32);
+ check_neg_no_wrap!(0xfedc_ba98_7654_3217_u64 as i64);
+ check_neg_no_wrap!(0xfedc_ba98_7654_3217_u64 as u64 as isize);
+
+ check_neg_wraps!(0x80_u8 as i8);
+ check_neg_wraps!(0x8000_u16 as i16);
+ check_neg_wraps!(0x8000_0000_u32 as i32);
+ check_neg_wraps!(0x8000_0000_0000_0000_u64 as i64);
+ match () {
+ #[cfg(target_pointer_width = "32")]
+ () => {
+ check_neg_wraps!(0x8000_0000_u32 as isize);
+ }
+ #[cfg(target_pointer_width = "64")]
+ () => {
+ check_neg_wraps!(0x8000_0000_0000_0000_u64 as isize);
+ }
+ }
+}
+
+#[test]
+fn wrapping_const() {
+ // Specifically the wrapping behavior of division and remainder is subtle,
+ // see https://github.com/rust-lang/rust/pull/94512.
+ const _: () = {
+ assert!(i32::MIN.wrapping_div(-1) == i32::MIN);
+ assert!(i32::MIN.wrapping_rem(-1) == 0);
+ };
+}