]> git.lizzy.rs Git - rust.git/blob - library/core/tests/num/flt2dec/random.rs
Rollup merge of #106823 - m-ou-se:format-args-as-str-guarantees, r=dtolnay
[rust.git] / library / core / tests / num / flt2dec / random.rs
1 #![cfg(not(target_arch = "wasm32"))]
2
3 use std::mem::MaybeUninit;
4 use std::str;
5
6 use core::num::flt2dec::strategy::grisu::format_exact_opt;
7 use core::num::flt2dec::strategy::grisu::format_shortest_opt;
8 use core::num::flt2dec::MAX_SIG_DIGITS;
9 use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded};
10
11 use rand::distributions::{Distribution, Uniform};
12
13 pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
14     match decode(v).1 {
15         FullDecoded::Finite(decoded) => decoded,
16         full_decoded => panic!("expected finite, got {full_decoded:?} instead"),
17     }
18 }
19
20 fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
21 where
22     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
23     G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
24     V: FnMut(usize) -> Decoded,
25 {
26     assert!(k <= 1024);
27
28     let mut npassed = 0; // f(x) = Some(g(x))
29     let mut nignored = 0; // f(x) = None
30
31     for i in 0..n {
32         if (i & 0xfffff) == 0 {
33             println!(
34                 "in progress, {:x}/{:x} (ignored={} passed={} failed={})",
35                 i,
36                 n,
37                 nignored,
38                 npassed,
39                 i - nignored - npassed
40             );
41         }
42
43         let decoded = v(i);
44         let mut buf1 = [MaybeUninit::new(0); 1024];
45         if let Some((buf1, e1)) = f(&decoded, &mut buf1[..k]) {
46             let mut buf2 = [MaybeUninit::new(0); 1024];
47             let (buf2, e2) = g(&decoded, &mut buf2[..k]);
48             if e1 == e2 && buf1 == buf2 {
49                 npassed += 1;
50             } else {
51                 println!(
52                     "equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
53                     i,
54                     n,
55                     decoded,
56                     str::from_utf8(buf1).unwrap(),
57                     e1,
58                     str::from_utf8(buf2).unwrap(),
59                     e2
60                 );
61             }
62         } else {
63             nignored += 1;
64         }
65     }
66     println!(
67         "{}({}): done, ignored={} passed={} failed={}",
68         func,
69         k,
70         nignored,
71         npassed,
72         n - nignored - npassed
73     );
74     assert!(
75         nignored + npassed == n,
76         "{}({}): {} out of {} values returns an incorrect value!",
77         func,
78         k,
79         n - nignored - npassed,
80         n
81     );
82     (npassed, nignored)
83 }
84
85 pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
86 where
87     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
88     G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
89 {
90     if cfg!(target_os = "emscripten") {
91         return; // using rng pulls in i128 support, which doesn't work
92     }
93     let mut rng = crate::test_rng();
94     let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000);
95     iterate("f32_random_equivalence_test", k, n, f, g, |_| {
96         let x = f32::from_bits(f32_range.sample(&mut rng));
97         decode_finite(x)
98     });
99 }
100
101 pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
102 where
103     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
104     G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
105 {
106     if cfg!(target_os = "emscripten") {
107         return; // using rng pulls in i128 support, which doesn't work
108     }
109     let mut rng = crate::test_rng();
110     let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
111     iterate("f64_random_equivalence_test", k, n, f, g, |_| {
112         let x = f64::from_bits(f64_range.sample(&mut rng));
113         decode_finite(x)
114     });
115 }
116
117 pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
118 where
119     F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
120     G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
121 {
122     // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
123     // so why not simply testing all of them?
124     //
125     // this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
126     // but with `-C opt-level=3 -C lto` this only takes about an hour or so.
127
128     // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e., all finite ranges
129     let (npassed, nignored) =
130         iterate("f32_exhaustive_equivalence_test", k, 0x7f7f_ffff, f, g, |i: usize| {
131             let x = f32::from_bits(i as u32 + 1);
132             decode_finite(x)
133         });
134     assert_eq!((npassed, nignored), (2121451881, 17643158));
135 }
136
137 #[test]
138 fn shortest_random_equivalence_test() {
139     use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
140     // Miri is too slow
141     let n = if cfg!(miri) { 10 } else { 10_000 };
142
143     f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
144     f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
145 }
146
147 #[test]
148 #[ignore] // it is too expensive
149 fn shortest_f32_exhaustive_equivalence_test() {
150     // it is hard to directly test the optimality of the output, but we can at least test if
151     // two different algorithms agree to each other.
152     //
153     // this reports the progress and the number of f32 values returned `None`.
154     // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
155     // `done, ignored=17643158 passed=2121451881 failed=0`.
156
157     use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
158     f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
159 }
160
161 #[test]
162 #[ignore] // it is too expensive
163 fn shortest_f64_hard_random_equivalence_test() {
164     // this again probably has to use appropriate rustc flags.
165
166     use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
167     f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000);
168 }
169
170 #[test]
171 fn exact_f32_random_equivalence_test() {
172     use core::num::flt2dec::strategy::dragon::format_exact as fallback;
173     // Miri is too slow
174     let n = if cfg!(miri) { 3 } else { 1_000 };
175
176     for k in 1..21 {
177         f32_random_equivalence_test(
178             |d, buf| format_exact_opt(d, buf, i16::MIN),
179             |d, buf| fallback(d, buf, i16::MIN),
180             k,
181             n,
182         );
183     }
184 }
185
186 #[test]
187 fn exact_f64_random_equivalence_test() {
188     use core::num::flt2dec::strategy::dragon::format_exact as fallback;
189     // Miri is too slow
190     let n = if cfg!(miri) { 2 } else { 1_000 };
191
192     for k in 1..21 {
193         f64_random_equivalence_test(
194             |d, buf| format_exact_opt(d, buf, i16::MIN),
195             |d, buf| fallback(d, buf, i16::MIN),
196             k,
197             n,
198         );
199     }
200 }