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