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