1 #![cfg(not(target_arch = "wasm32"))]
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};
10 use rand::distributions::{Distribution, Uniform};
11 use rand::rngs::StdRng;
12 use rand::SeedableRng;
14 pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
16 FullDecoded::Finite(decoded) => decoded,
17 full_decoded => panic!("expected finite, got {:?} instead", full_decoded),
21 fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
23 F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
24 G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
25 V: FnMut(usize) -> Decoded,
29 let mut npassed = 0; // f(x) = Some(g(x))
30 let mut nignored = 0; // f(x) = None
33 if (i & 0xfffff) == 0 {
35 "in progress, {:x}/{:x} (ignored={} passed={} failed={})",
40 i - nignored - npassed
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] {
53 "equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
57 str::from_utf8(&buf1[..len1]).unwrap(),
59 str::from_utf8(&buf2[..len2]).unwrap(),
68 "{}({}): done, ignored={} passed={} failed={}",
73 n - nignored - npassed
76 nignored + npassed == n,
77 "{}({}): {} out of {} values returns an incorrect value!",
80 n - nignored - npassed,
86 pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
88 F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
89 G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
91 if cfg!(target_os = "emscripten") {
92 return; // using rng pulls in i128 support, which doesn't work
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));
102 pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
104 F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
105 G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
107 if cfg!(target_os = "emscripten") {
108 return; // using rng pulls in i128 support, which doesn't work
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));
118 pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
120 F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
121 G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
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?
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.
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);
135 assert_eq!((npassed, nignored), (2121451881, 17643158));
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;
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);
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.
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`.
160 use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
161 f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
165 #[ignore] // it is too expensive
166 fn shortest_f64_hard_random_equivalence_test() {
167 // this again probably has to use appropriate rustc flags.
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);
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;
182 f32_random_equivalence_test(
183 |d, buf| format_exact_opt(d, buf, i16::MIN),
184 |d, buf| fallback(d, buf, i16::MIN),
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;
200 f64_random_equivalence_test(
201 |d, buf| format_exact_opt(d, buf, i16::MIN),
202 |d, buf| fallback(d, buf, i16::MIN),