2017-10-22 20:01:00 -07:00
|
|
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2018-02-26 09:07:16 -08:00
|
|
|
#![cfg(not(target_arch = "wasm32"))]
|
2017-10-22 20:01:00 -07:00
|
|
|
|
|
|
|
use std::i16;
|
|
|
|
use std::mem;
|
|
|
|
use std::str;
|
|
|
|
|
|
|
|
use core::num::flt2dec::MAX_SIG_DIGITS;
|
|
|
|
use core::num::flt2dec::strategy::grisu::format_exact_opt;
|
|
|
|
use core::num::flt2dec::strategy::grisu::format_shortest_opt;
|
|
|
|
use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};
|
|
|
|
|
2018-02-26 09:07:16 -08:00
|
|
|
use rand::{self, Rand, XorShiftRng};
|
2017-10-22 20:01:00 -07:00
|
|
|
use rand::distributions::{IndependentSample, Range};
|
2018-02-26 09:07:16 -08:00
|
|
|
|
2017-10-22 20:01:00 -07:00
|
|
|
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
|
|
|
|
match decode(v).1 {
|
|
|
|
FullDecoded::Finite(decoded) => decoded,
|
|
|
|
full_decoded => panic!("expected finite, got {:?} instead", full_decoded)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
|
|
|
|
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
|
|
|
G: FnMut(&Decoded, &mut [u8]) -> (usize, 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 = [0; 1024];
|
|
|
|
if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
|
|
|
|
let mut buf2 = [0; 1024];
|
|
|
|
let (len2, e2) = g(&decoded, &mut buf2[..k]);
|
|
|
|
if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
|
|
|
|
npassed += 1;
|
|
|
|
} else {
|
|
|
|
println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
|
|
|
|
i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
|
|
|
|
str::from_utf8(&buf2[..len2]).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: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
|
|
|
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
|
|
|
|
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
|
|
|
|
let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
|
|
|
|
iterate("f32_random_equivalence_test", k, n, f, g, |_| {
|
|
|
|
let i: u32 = f32_range.ind_sample(&mut rng);
|
|
|
|
let x: f32 = unsafe {mem::transmute(i)};
|
|
|
|
decode_finite(x)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
|
|
|
|
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
|
|
|
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
|
|
|
|
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
|
|
|
|
let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
|
|
|
|
iterate("f64_random_equivalence_test", k, n, f, g, |_| {
|
|
|
|
let i: u64 = f64_range.ind_sample(&mut rng);
|
|
|
|
let x: f64 = unsafe {mem::transmute(i)};
|
|
|
|
decode_finite(x)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
|
|
|
|
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
|
|
|
|
G: FnMut(&Decoded, &mut [u8]) -> (usize, 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 = unsafe {mem::transmute(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;
|
|
|
|
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
|
|
|
|
f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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;
|
|
|
|
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, 1_000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn exact_f64_random_equivalence_test() {
|
|
|
|
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
|
|
|
|
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, 1_000);
|
|
|
|
}
|
|
|
|
}
|
2018-02-26 09:07:16 -08:00
|
|
|
|