about summary refs log tree commit diff
path: root/src/etc
diff options
context:
space:
mode:
authoronur-ozkan <work@onurozkan.dev>2025-06-03 10:38:15 +0300
committeronur-ozkan <work@onurozkan.dev>2025-06-03 11:05:51 +0300
commit59fbe04a5249d2da56b19bf0e7f4f017064a3866 (patch)
tree7c6477a240400851fc48d08e0d53eb21a51fb6be /src/etc
parent99426c570eebec8dcba2eaa8f5057265346aaedc (diff)
downloadrust-59fbe04a5249d2da56b19bf0e7f4f017064a3866.tar.gz
rust-59fbe04a5249d2da56b19bf0e7f4f017064a3866.zip
move `test-float-parse` tool into `src/tools` dir
Obviously `test-float-parse` is a tool like any other in `src/tools`.

Signed-off-by: onur-ozkan <work@onurozkan.dev>
Diffstat (limited to 'src/etc')
-rw-r--r--src/etc/test-float-parse/Cargo.lock75
-rw-r--r--src/etc/test-float-parse/Cargo.toml22
-rw-r--r--src/etc/test-float-parse/README.md55
-rw-r--r--src/etc/test-float-parse/src/gen_/exhaustive.rs42
-rw-r--r--src/etc/test-float-parse/src/gen_/exponents.rs95
-rw-r--r--src/etc/test-float-parse/src/gen_/fuzz.rs87
-rw-r--r--src/etc/test-float-parse/src/gen_/integers.rs104
-rw-r--r--src/etc/test-float-parse/src/gen_/long_fractions.rs58
-rw-r--r--src/etc/test-float-parse/src/gen_/many_digits.rs84
-rw-r--r--src/etc/test-float-parse/src/gen_/sparse.rs99
-rw-r--r--src/etc/test-float-parse/src/gen_/spot_checks.rs101
-rw-r--r--src/etc/test-float-parse/src/gen_/subnorm.rs108
-rw-r--r--src/etc/test-float-parse/src/lib.rs419
-rw-r--r--src/etc/test-float-parse/src/main.rs129
-rw-r--r--src/etc/test-float-parse/src/traits.rs206
-rw-r--r--src/etc/test-float-parse/src/ui.rs168
-rw-r--r--src/etc/test-float-parse/src/validate.rs394
-rw-r--r--src/etc/test-float-parse/src/validate/tests.rs149
18 files changed, 0 insertions, 2395 deletions
diff --git a/src/etc/test-float-parse/Cargo.lock b/src/etc/test-float-parse/Cargo.lock
deleted file mode 100644
index 3f60423fed3..00000000000
--- a/src/etc/test-float-parse/Cargo.lock
+++ /dev/null
@@ -1,75 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "getrandom"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.147"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "test-float-parse"
-version = "0.1.0"
-dependencies = [
- "rand",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/src/etc/test-float-parse/Cargo.toml b/src/etc/test-float-parse/Cargo.toml
deleted file mode 100644
index e407e322f9e..00000000000
--- a/src/etc/test-float-parse/Cargo.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-[package]
-name = "test-float-parse"
-version = "0.1.0"
-edition = "2024"
-publish = false
-
-[dependencies]
-indicatif = { version = "0.17.8", default-features = false }
-num = "0.4.3"
-rand = "0.9.0"
-rand_chacha = "0.9.0"
-rayon = "1"
-
-[lib]
-name = "test_float_parse"
-
-[lints.rust.unexpected_cfgs]
-level = "warn"
-check-cfg = [
-    # Internal features aren't marked known config by default
-    'cfg(target_has_reliable_f16)',
-]
diff --git a/src/etc/test-float-parse/README.md b/src/etc/test-float-parse/README.md
deleted file mode 100644
index 5e2c43d1cad..00000000000
--- a/src/etc/test-float-parse/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Float Parsing Tests
-
-These are tests designed to test decimal to float conversions (`dec2flt`) used
-by the standard library.
-
-It consists of a collection of test generators that each generate a set of
-patterns intended to test a specific property. In addition, there are exhaustive
-tests (for <= `f32`) and fuzzers (for anything that can't be run exhaustively).
-
-The generators work as follows:
-
-- Each generator is a struct that lives somewhere in the `gen` module. Usually
-  it is generic over a float type.
-- These generators must implement `Iterator`, which should return a context type
-  that can be used to construct a test string (but usually not the string
-  itself).
-- They must also implement the `Generator` trait, which provides a method to
-  write test context to a string as a test case, as well as some extra metadata.
-
-  The split between context generation and string construction is so that we can
-  reuse string allocations.
-- Each generator gets registered once for each float type. Each of these
-  generators then get their iterator called, and each test case checked against
-  the float type's parse implementation.
-
-Some generators produce decimal strings, others create bit patterns that need to
-be bitcasted to the float type, which then uses its `Display` implementation to
-write to a string. For these, float to decimal (`flt2dec`) conversions also get
-tested, if unintentionally.
-
-For each test case, the following is done:
-
-- The test string is parsed to the float type using the standard library's
-  implementation.
-- The test string is parsed separately to a `BigRational`, which acts as a
-  representation with infinite precision.
-- The rational value then gets checked that it is within the float's
-  representable values (absolute value greater than the smallest number to round
-  to zero, but less less than the first value to round to infinity). If these
-  limits are exceeded, check that the parsed float reflects that.
-- For real nonzero numbers, the parsed float is converted into a rational using
-  `significand * 2^exponent`. It is then checked against the actual rational
-  value, and verified to be within half a bit's precision of the parsed value.
-  Also it is checked that ties round to even.
-
-This is all highly parallelized with `rayon`; test generators can run in
-parallel, and their tests get chunked and run in parallel.
-
-There is a simple command line that allows filtering which tests are run,
-setting the number of iterations for fuzzing tests, limiting failures, setting
-timeouts, etc. See `main.rs` or run with `--help` for options.
-
-Note that when running via `./x`, only tests that take less than a few minutes
-are run by default. Navigate to the crate (or pass `-C` to Cargo) and run it
-directly to run all tests or pass specific arguments.
diff --git a/src/etc/test-float-parse/src/gen_/exhaustive.rs b/src/etc/test-float-parse/src/gen_/exhaustive.rs
deleted file mode 100644
index 01458fb0b60..00000000000
--- a/src/etc/test-float-parse/src/gen_/exhaustive.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use std::fmt::Write;
-use std::ops::RangeInclusive;
-
-use crate::{Float, Generator, Int};
-
-/// Test every possible bit pattern. This is infeasible to run on any float types larger than
-/// `f32` (which takes about an hour).
-pub struct Exhaustive<F: Float> {
-    iter: RangeInclusive<F::Int>,
-}
-
-impl<F: Float> Generator<F> for Exhaustive<F>
-where
-    RangeInclusive<F::Int>: Iterator<Item = F::Int>,
-{
-    const SHORT_NAME: &'static str = "exhaustive";
-
-    type WriteCtx = F;
-
-    fn total_tests() -> u64 {
-        1u64.checked_shl(F::Int::BITS).expect("More than u64::MAX tests")
-    }
-
-    fn new() -> Self {
-        Self { iter: F::Int::ZERO..=F::Int::MAX }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx:e}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for Exhaustive<F>
-where
-    RangeInclusive<F::Int>: Iterator<Item = F::Int>,
-{
-    type Item = F;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        Some(F::from_bits(self.iter.next()?))
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/exponents.rs b/src/etc/test-float-parse/src/gen_/exponents.rs
deleted file mode 100644
index 3748e9d380c..00000000000
--- a/src/etc/test-float-parse/src/gen_/exponents.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use std::fmt::Write;
-use std::ops::RangeInclusive;
-
-use crate::traits::BoxGenIter;
-use crate::{Float, Generator};
-
-const SMALL_COEFF_MAX: i32 = 10_000;
-const SMALL_EXP_MAX: i32 = 300;
-
-const SMALL_COEFF_RANGE: RangeInclusive<i32> = (-SMALL_COEFF_MAX)..=SMALL_COEFF_MAX;
-const SMALL_EXP_RANGE: RangeInclusive<i32> = (-SMALL_EXP_MAX)..=SMALL_EXP_MAX;
-
-const LARGE_COEFF_RANGE: RangeInclusive<u32> = 0..=100_000;
-const LARGE_EXP_RANGE: RangeInclusive<u32> = 300..=350;
-
-/// Check exponential values around zero.
-pub struct SmallExponents<F: Float> {
-    iter: BoxGenIter<Self, F>,
-}
-
-impl<F: Float> Generator<F> for SmallExponents<F> {
-    const NAME: &'static str = "small exponents";
-    const SHORT_NAME: &'static str = "small exp";
-
-    /// `(coefficient, exponent)`
-    type WriteCtx = (i32, i32);
-
-    fn total_tests() -> u64 {
-        ((1 + SMALL_COEFF_RANGE.end() - SMALL_COEFF_RANGE.start())
-            * (1 + SMALL_EXP_RANGE.end() - SMALL_EXP_RANGE.start()))
-        .try_into()
-        .unwrap()
-    }
-
-    fn new() -> Self {
-        let iter = SMALL_EXP_RANGE.flat_map(|exp| SMALL_COEFF_RANGE.map(move |coeff| (coeff, exp)));
-
-        Self { iter: Box::new(iter) }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        let (coeff, exp) = ctx;
-        write!(s, "{coeff}e{exp}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for SmallExponents<F> {
-    type Item = (i32, i32);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
-
-/// Check exponential values further from zero.
-pub struct LargeExponents<F: Float> {
-    iter: BoxGenIter<Self, F>,
-}
-
-impl<F: Float> Generator<F> for LargeExponents<F> {
-    const NAME: &'static str = "large positive exponents";
-    const SHORT_NAME: &'static str = "large exp";
-
-    /// `(coefficient, exponent, is_positive)`
-    type WriteCtx = (u32, u32, bool);
-
-    fn total_tests() -> u64 {
-        ((1 + LARGE_EXP_RANGE.end() - LARGE_EXP_RANGE.start())
-            * (1 + LARGE_COEFF_RANGE.end() - LARGE_COEFF_RANGE.start())
-            * 2)
-        .into()
-    }
-
-    fn new() -> Self {
-        let iter = LARGE_EXP_RANGE
-            .flat_map(|exp| LARGE_COEFF_RANGE.map(move |coeff| (coeff, exp)))
-            .flat_map(|(coeff, exp)| [(coeff, exp, false), (coeff, exp, true)]);
-
-        Self { iter: Box::new(iter) }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        let (coeff, exp, is_positive) = ctx;
-        let sign = if is_positive { "" } else { "-" };
-        write!(s, "{sign}{coeff}e{exp}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for LargeExponents<F> {
-    type Item = (u32, u32, bool);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/fuzz.rs b/src/etc/test-float-parse/src/gen_/fuzz.rs
deleted file mode 100644
index 1d6c5562a14..00000000000
--- a/src/etc/test-float-parse/src/gen_/fuzz.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use std::any::{TypeId, type_name};
-use std::collections::BTreeMap;
-use std::fmt::Write;
-use std::marker::PhantomData;
-use std::ops::Range;
-use std::sync::Mutex;
-
-use rand::Rng;
-use rand::distr::{Distribution, StandardUniform};
-use rand_chacha::ChaCha8Rng;
-use rand_chacha::rand_core::SeedableRng;
-
-use crate::{Float, Generator, Int, SEED};
-
-/// Mapping of float types to the number of iterations that should be run.
-///
-/// We could probably make `Generator::new` take an argument instead of the global state,
-/// but we only load this once so it works.
-static FUZZ_COUNTS: Mutex<BTreeMap<TypeId, u64>> = Mutex::new(BTreeMap::new());
-
-/// Generic fuzzer; just tests deterministic random bit patterns N times.
-pub struct Fuzz<F> {
-    iter: Range<u64>,
-    rng: ChaCha8Rng,
-    /// Allow us to use generics in `Iterator`.
-    marker: PhantomData<F>,
-}
-
-impl<F: Float> Fuzz<F> {
-    /// Register how many iterations the fuzzer should run for a type. Uses some logic by
-    /// default, but if `from_cfg` is `Some`, that will be used instead.
-    pub fn set_iterations(from_cfg: Option<u64>) {
-        let count = if let Some(cfg_count) = from_cfg {
-            cfg_count
-        } else if F::BITS <= crate::MAX_BITS_FOR_EXHAUUSTIVE {
-            // If we run exhaustively, still fuzz but only do half as many bits. The only goal here is
-            // to catch failures from e.g. high bit patterns before exhaustive tests would get to them.
-            (F::Int::MAX >> (F::BITS / 2)).try_into().unwrap()
-        } else {
-            // Eveything bigger gets a fuzz test with as many iterations as `f32` exhaustive.
-            u32::MAX.into()
-        };
-
-        let _ = FUZZ_COUNTS.lock().unwrap().insert(TypeId::of::<F>(), count);
-    }
-}
-
-impl<F: Float> Generator<F> for Fuzz<F>
-where
-    StandardUniform: Distribution<<F as Float>::Int>,
-{
-    const SHORT_NAME: &'static str = "fuzz";
-
-    type WriteCtx = F;
-
-    fn total_tests() -> u64 {
-        *FUZZ_COUNTS
-            .lock()
-            .unwrap()
-            .get(&TypeId::of::<F>())
-            .unwrap_or_else(|| panic!("missing fuzz count for {}", type_name::<F>()))
-    }
-
-    fn new() -> Self {
-        let rng = ChaCha8Rng::from_seed(SEED);
-
-        Self { iter: 0..Self::total_tests(), rng, marker: PhantomData }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx:e}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for Fuzz<F>
-where
-    StandardUniform: Distribution<<F as Float>::Int>,
-{
-    type Item = <Self as Generator<F>>::WriteCtx;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let _ = self.iter.next()?;
-        let i: F::Int = self.rng.random();
-
-        Some(F::from_bits(i))
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/integers.rs b/src/etc/test-float-parse/src/gen_/integers.rs
deleted file mode 100644
index 070d188e88c..00000000000
--- a/src/etc/test-float-parse/src/gen_/integers.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-use std::fmt::Write;
-use std::ops::{Range, RangeInclusive};
-
-use crate::traits::BoxGenIter;
-use crate::{Float, Generator};
-
-const SMALL_MAX_POW2: u32 = 19;
-
-/// All values up to the max power of two
-const SMALL_VALUES: RangeInclusive<i32> = {
-    let max = 1i32 << SMALL_MAX_POW2;
-    (-max)..=max
-};
-
-/// Large values only get tested around powers of two
-const LARGE_POWERS: Range<u32> = SMALL_MAX_POW2..128;
-
-/// We perturbe each large value around these ranges
-const LARGE_PERTURBATIONS: RangeInclusive<i128> = -256..=256;
-
-/// Test all integers up to `2 ^ MAX_POW2`
-pub struct SmallInt {
-    iter: RangeInclusive<i32>,
-}
-
-impl<F: Float> Generator<F> for SmallInt {
-    const NAME: &'static str = "small integer values";
-    const SHORT_NAME: &'static str = "int small";
-
-    type WriteCtx = i32;
-
-    fn total_tests() -> u64 {
-        (SMALL_VALUES.end() + 1 - SMALL_VALUES.start()).try_into().unwrap()
-    }
-
-    fn new() -> Self {
-        Self { iter: SMALL_VALUES }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx}").unwrap();
-    }
-}
-
-impl Iterator for SmallInt {
-    type Item = i32;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
-
-/// Test much bigger integers than [`SmallInt`].
-pub struct LargeInt<F: Float> {
-    iter: BoxGenIter<Self, F>,
-}
-
-impl<F: Float> LargeInt<F> {
-    const EDGE_CASES: [i128; 7] = [
-        i32::MIN as i128,
-        i32::MAX as i128,
-        i64::MIN as i128,
-        i64::MAX as i128,
-        u64::MAX as i128,
-        i128::MIN,
-        i128::MAX,
-    ];
-}
-
-impl<F: Float> Generator<F> for LargeInt<F> {
-    const NAME: &'static str = "large integer values";
-    const SHORT_NAME: &'static str = "int large";
-
-    type WriteCtx = i128;
-
-    fn total_tests() -> u64 {
-        u64::try_from(
-            (i128::from(LARGE_POWERS.end - LARGE_POWERS.start)
-                + i128::try_from(Self::EDGE_CASES.len()).unwrap())
-                * (LARGE_PERTURBATIONS.end() + 1 - LARGE_PERTURBATIONS.start()),
-        )
-        .unwrap()
-    }
-
-    fn new() -> Self {
-        let iter = LARGE_POWERS
-            .map(|pow| 1i128 << pow)
-            .chain(Self::EDGE_CASES)
-            .flat_map(|base| LARGE_PERTURBATIONS.map(move |perturb| base.saturating_add(perturb)));
-
-        Self { iter: Box::new(iter) }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx}").unwrap();
-    }
-}
-impl<F: Float> Iterator for LargeInt<F> {
-    type Item = i128;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/long_fractions.rs b/src/etc/test-float-parse/src/gen_/long_fractions.rs
deleted file mode 100644
index b75148b779c..00000000000
--- a/src/etc/test-float-parse/src/gen_/long_fractions.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use std::char;
-use std::fmt::Write;
-
-use crate::{Float, Generator};
-
-/// Number of decimal digits to check (all of them).
-const MAX_DIGIT: u32 = 9;
-/// Test with this many decimals in the string.
-const MAX_DECIMALS: usize = 410;
-const PREFIX: &str = "0.";
-
-/// Test e.g. `0.1`, `0.11`, `0.111`, `0.1111`, ..., `0.2`, `0.22`, ...
-pub struct RepeatingDecimal {
-    digit: u32,
-    buf: String,
-}
-
-impl<F: Float> Generator<F> for RepeatingDecimal {
-    const NAME: &'static str = "repeating decimal";
-    const SHORT_NAME: &'static str = "dec rep";
-
-    type WriteCtx = String;
-
-    fn total_tests() -> u64 {
-        u64::from(MAX_DIGIT + 1) * u64::try_from(MAX_DECIMALS + 1).unwrap() + 1
-    }
-
-    fn new() -> Self {
-        Self { digit: 0, buf: PREFIX.to_owned() }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        *s = ctx;
-    }
-}
-
-impl Iterator for RepeatingDecimal {
-    type Item = String;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.digit > MAX_DIGIT {
-            return None;
-        }
-
-        let digit = self.digit;
-        let inc_digit = self.buf.len() - PREFIX.len() > MAX_DECIMALS;
-
-        if inc_digit {
-            // Reset the string
-            self.buf.clear();
-            self.digit += 1;
-            self.buf.write_str(PREFIX).unwrap();
-        }
-
-        self.buf.push(char::from_digit(digit, 10).unwrap());
-        Some(self.buf.clone())
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/many_digits.rs b/src/etc/test-float-parse/src/gen_/many_digits.rs
deleted file mode 100644
index 741e11437fe..00000000000
--- a/src/etc/test-float-parse/src/gen_/many_digits.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use std::char;
-use std::fmt::Write;
-use std::marker::PhantomData;
-use std::ops::{Range, RangeInclusive};
-
-use rand::distr::{Distribution, Uniform};
-use rand::{Rng, SeedableRng};
-use rand_chacha::ChaCha8Rng;
-
-use crate::{Float, Generator, SEED};
-
-/// Total iterations
-const ITERATIONS: u64 = 5_000_000;
-
-/// Possible lengths of the string, excluding decimals and exponents
-const POSSIBLE_NUM_DIGITS: RangeInclusive<usize> = 100..=400;
-
-/// Range of possible exponents
-const EXP_RANGE: Range<i32> = -4500..4500;
-
-/// Try strings of random digits.
-pub struct RandDigits<F> {
-    rng: ChaCha8Rng,
-    iter: Range<u64>,
-    uniform: Uniform<u32>,
-    /// Allow us to use generics in `Iterator`.
-    marker: PhantomData<F>,
-}
-
-impl<F: Float> Generator<F> for RandDigits<F> {
-    const NAME: &'static str = "random digits";
-
-    const SHORT_NAME: &'static str = "rand digits";
-
-    type WriteCtx = String;
-
-    fn total_tests() -> u64 {
-        ITERATIONS
-    }
-
-    fn new() -> Self {
-        let rng = ChaCha8Rng::from_seed(SEED);
-        let range = Uniform::try_from(0..10).unwrap();
-
-        Self { rng, iter: 0..ITERATIONS, uniform: range, marker: PhantomData }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        *s = ctx;
-    }
-}
-
-impl<F: Float> Iterator for RandDigits<F> {
-    type Item = String;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let _ = self.iter.next()?;
-        let num_digits = self.rng.random_range(POSSIBLE_NUM_DIGITS);
-        let has_decimal = self.rng.random_bool(0.2);
-        let has_exp = self.rng.random_bool(0.2);
-
-        let dec_pos = if has_decimal { Some(self.rng.random_range(0..num_digits)) } else { None };
-
-        let mut s = String::with_capacity(num_digits);
-
-        for pos in 0..num_digits {
-            let digit = char::from_digit(self.uniform.sample(&mut self.rng), 10).unwrap();
-            s.push(digit);
-
-            if let Some(dec_pos) = dec_pos {
-                if pos == dec_pos {
-                    s.push('.');
-                }
-            }
-        }
-
-        if has_exp {
-            let exp = self.rng.random_range(EXP_RANGE);
-            write!(s, "e{exp}").unwrap();
-        }
-
-        Some(s)
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/sparse.rs b/src/etc/test-float-parse/src/gen_/sparse.rs
deleted file mode 100644
index 72b65d4ce7f..00000000000
--- a/src/etc/test-float-parse/src/gen_/sparse.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use std::fmt::Write;
-
-use crate::traits::BoxGenIter;
-use crate::{Float, Generator};
-
-const POWERS_OF_TWO: [u128; 128] = make_powers_of_two();
-
-const fn make_powers_of_two() -> [u128; 128] {
-    let mut ret = [0; 128];
-    let mut i = 0;
-    while i < 128 {
-        ret[i] = 1 << i;
-        i += 1;
-    }
-
-    ret
-}
-
-/// Can't clone this result because of lifetime errors, just use a macro.
-macro_rules! pow_iter {
-    () => {
-        (0..F::BITS).map(|i| F::Int::try_from(POWERS_OF_TWO[i as usize]).unwrap())
-    };
-}
-
-/// Test all numbers that include three 1s in the binary representation as integers.
-pub struct FewOnesInt<F: Float>
-where
-    FewOnesInt<F>: Generator<F>,
-{
-    iter: BoxGenIter<Self, F>,
-}
-
-impl<F: Float> Generator<F> for FewOnesInt<F>
-where
-    <F::Int as TryFrom<u128>>::Error: std::fmt::Debug,
-{
-    const SHORT_NAME: &'static str = "few ones int";
-
-    type WriteCtx = F::Int;
-
-    fn total_tests() -> u64 {
-        u64::from(F::BITS).pow(3)
-    }
-
-    fn new() -> Self {
-        let iter = pow_iter!()
-            .flat_map(move |a| pow_iter!().map(move |b| (a, b)))
-            .flat_map(move |(a, b)| pow_iter!().map(move |c| (a, b, c)))
-            .map(|(a, b, c)| a | b | c);
-
-        Self { iter: Box::new(iter) }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for FewOnesInt<F> {
-    type Item = F::Int;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
-
-/// Similar to `FewOnesInt` except test those bit patterns as a float.
-pub struct FewOnesFloat<F: Float>(FewOnesInt<F>);
-
-impl<F: Float> Generator<F> for FewOnesFloat<F>
-where
-    <F::Int as TryFrom<u128>>::Error: std::fmt::Debug,
-{
-    const NAME: &'static str = "few ones float";
-    const SHORT_NAME: &'static str = "few ones float";
-
-    type WriteCtx = F;
-
-    fn total_tests() -> u64 {
-        FewOnesInt::<F>::total_tests()
-    }
-
-    fn new() -> Self {
-        Self(FewOnesInt::new())
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx:e}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for FewOnesFloat<F> {
-    type Item = F;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next().map(|i| F::from_bits(i))
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/spot_checks.rs b/src/etc/test-float-parse/src/gen_/spot_checks.rs
deleted file mode 100644
index 18691f9d6cf..00000000000
--- a/src/etc/test-float-parse/src/gen_/spot_checks.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use std::fmt::Write;
-
-use crate::traits::{Float, Generator};
-
-const SPECIAL: &[&str] = &[
-    "inf", "Inf", "iNf", "INF", "-inf", "-Inf", "-iNf", "-INF", "+inf", "+Inf", "+iNf", "+INF",
-    "nan", "NaN", "NAN", "nAn", "-nan", "-NaN", "-NAN", "-nAn", "+nan", "+NaN", "+NAN", "+nAn",
-    "1", "-1", "+1", "1e1", "-1e1", "+1e1", "1e-1", "-1e-1", "+1e-1", "1e+1", "-1e+1", "+1e+1",
-    "1E1", "-1E1", "+1E1", "1E-1", "-1E-1", "+1E-1", "1E+1", "-1E+1", "+1E+1", "0", "-0", "+0",
-];
-
-/// Check various non-numeric special strings.
-pub struct Special {
-    iter: std::slice::Iter<'static, &'static str>,
-}
-
-impl<F: Float> Generator<F> for Special {
-    const NAME: &'static str = "special values";
-
-    const SHORT_NAME: &'static str = "special";
-
-    type WriteCtx = &'static str;
-
-    fn total_tests() -> u64 {
-        SPECIAL.len().try_into().unwrap()
-    }
-
-    fn new() -> Self {
-        Self { iter: SPECIAL.iter() }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        s.write_str(ctx).unwrap();
-    }
-}
-
-impl Iterator for Special {
-    type Item = &'static str;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().copied()
-    }
-}
-
-/// Strings that we know have failed in the past
-const REGRESSIONS: &[&str] = &[
-    // From <https://github.com/rust-lang/rust/issues/31407>
-    "1234567890123456789012345678901234567890e-340",
-    "2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308",
-    "2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875e-308",
-    "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000222507385850720138309023271733240406421921598046233183055332741688720443481391819585428315901251102056406733973103581100515243416155346010885601238537771882113077799353200233047961014744258363607192156504694250373420837525080665061665815894872049117996859163964850063590877011830487479978088775374994945158045160505091539985658247081864511353793580499211598108576605199243335211435239014879569960959128889160299264151106346631339366347758651302937176204732563178148566435087212282863764204484681140761391147706280168985324411002416144742161856716615054015428508471675290190316132277889672970737312333408698898317506783884692609277397797285865965494109136909540613646756870239867831529068098461721092462539672851562500000000000000001",
-    "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.9999999999999999999999999999999999999999999999999999999999999999999999",
-    "2.47032822920623272e-324",
-    "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316",
-    "3.237883913302901289588352412501532174863037669423108059901297049552301970670676565786835742587799557860615776559838283435514391084153169252689190564396459577394618038928365305143463955100356696665629202017331344031730044369360205258345803431471660032699580731300954848363975548690010751530018881758184174569652173110473696022749934638425380623369774736560008997404060967498028389191878963968575439222206416981462690113342524002724385941651051293552601421155333430225237291523843322331326138431477823591142408800030775170625915670728657003151953664260769822494937951845801530895238439819708403389937873241463484205608000027270531106827387907791444918534771598750162812548862768493201518991668028251730299953143924168545708663913273994694463908672332763671875E-319",
-    "6.953355807847677105972805215521891690222119817145950754416205607980030131549636688806115726399441880065386399864028691275539539414652831584795668560082999889551357784961446896042113198284213107935110217162654939802416034676213829409720583759540476786936413816541621287843248433202369209916612249676005573022703244799714622116542188837770376022371172079559125853382801396219552418839469770514904192657627060319372847562301074140442660237844114174497210955449896389180395827191602886654488182452409583981389442783377001505462015745017848754574668342161759496661766020028752888783387074850773192997102997936619876226688096314989645766000479009083731736585750335262099860150896718774401964796827166283225641992040747894382698751809812609536720628966577351093292236328125E-310",
-    "3.339068557571188581835713701280943911923401916998521771655656997328440314559615318168849149074662609099998113009465566426808170378434065722991659642619467706034884424989741080790766778456332168200464651593995817371782125010668346652995912233993254584461125868481633343674905074271064409763090708017856584019776878812425312008812326260363035474811532236853359905334625575404216060622858633280744301892470300555678734689978476870369853549413277156622170245846166991655321535529623870646888786637528995592800436177901746286272273374471701452991433047257863864601424252024791567368195056077320885329384322332391564645264143400798619665040608077549162173963649264049738362290606875883456826586710961041737908872035803481241600376705491726170293986797332763671875E-319",
-    "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324",
-    "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324",
-    "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-324",
-    "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999e-324",
-    "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375e-324",
-    "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001e-324",
-    "94393431193180696942841837085033647913224148539854e-358",
-    "104308485241983990666713401708072175773165034278685682646111762292409330928739751702404658197872319129036519947435319418387839758990478549477777586673075945844895981012024387992135617064532141489278815239849108105951619997829153633535314849999674266169258928940692239684771590065027025835804863585454872499320500023126142553932654370362024104462255244034053203998964360882487378334860197725139151265590832887433736189468858614521708567646743455601905935595381852723723645799866672558576993978025033590728687206296379801363024094048327273913079612469982585674824156000783167963081616214710691759864332339239688734656548790656486646106983450809073750535624894296242072010195710276073042036425579852459556183541199012652571123898996574563824424330960027873516082763671875e-1075",
-    "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281249",
-    "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281251",
-];
-
-/// Check items that failed in the past.
-pub struct RegressionCheck {
-    iter: std::slice::Iter<'static, &'static str>,
-}
-
-impl<F: Float> Generator<F> for RegressionCheck {
-    const NAME: &'static str = "regression check";
-
-    const SHORT_NAME: &'static str = "regression";
-
-    type WriteCtx = &'static str;
-
-    fn total_tests() -> u64 {
-        REGRESSIONS.len().try_into().unwrap()
-    }
-
-    fn new() -> Self {
-        Self { iter: REGRESSIONS.iter() }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        s.write_str(ctx).unwrap();
-    }
-}
-
-impl Iterator for RegressionCheck {
-    type Item = &'static str;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().copied()
-    }
-}
diff --git a/src/etc/test-float-parse/src/gen_/subnorm.rs b/src/etc/test-float-parse/src/gen_/subnorm.rs
deleted file mode 100644
index 654f324b9b0..00000000000
--- a/src/etc/test-float-parse/src/gen_/subnorm.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use std::fmt::Write;
-use std::ops::RangeInclusive;
-
-use crate::{Float, Generator, Int};
-
-/// Spot check some edge cases for subnormals.
-pub struct SubnormEdgeCases<F: Float> {
-    cases: [F::Int; 6],
-    index: usize,
-}
-
-impl<F: Float> SubnormEdgeCases<F> {
-    /// Shorthand
-    const I1: F::Int = F::Int::ONE;
-
-    fn edge_cases() -> [F::Int; 6] {
-        // Comments use an 8-bit mantissa as a demo
-        [
-            // 0b00000001
-            Self::I1,
-            // 0b10000000
-            Self::I1 << (F::MAN_BITS - 1),
-            // 0b00001000
-            Self::I1 << ((F::MAN_BITS / 2) - 1),
-            // 0b00001111
-            Self::I1 << ((F::MAN_BITS / 2) - 1),
-            // 0b00001111
-            Self::I1 << ((F::MAN_BITS / 2) - 1),
-            // 0b11111111
-            F::MAN_MASK,
-        ]
-    }
-}
-
-impl<F: Float> Generator<F> for SubnormEdgeCases<F> {
-    const NAME: &'static str = "subnormal edge cases";
-    const SHORT_NAME: &'static str = "subnorm edge";
-
-    type WriteCtx = F;
-
-    fn new() -> Self {
-        Self { cases: Self::edge_cases(), index: 0 }
-    }
-
-    fn total_tests() -> u64 {
-        Self::edge_cases().len().try_into().unwrap()
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx:e}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for SubnormEdgeCases<F> {
-    type Item = F;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let i = self.cases.get(self.index)?;
-        self.index += 1;
-
-        Some(F::from_bits(*i))
-    }
-}
-
-/// Test all subnormals up to `1 << 22`.
-pub struct SubnormComplete<F: Float> {
-    iter: RangeInclusive<F::Int>,
-}
-
-impl<F: Float> Generator<F> for SubnormComplete<F>
-where
-    RangeInclusive<F::Int>: Iterator<Item = F::Int>,
-{
-    const NAME: &'static str = "subnormal";
-    const SHORT_NAME: &'static str = "subnorm ";
-
-    type WriteCtx = F;
-
-    fn total_tests() -> u64 {
-        let iter = Self::new().iter;
-        (F::Int::ONE + *iter.end() - *iter.start()).try_into().unwrap()
-    }
-
-    fn new() -> Self {
-        let upper_lim = if F::MAN_BITS >= 22 {
-            F::Int::ONE << 22
-        } else {
-            (F::Int::ONE << F::MAN_BITS) - F::Int::ONE
-        };
-
-        Self { iter: F::Int::ZERO..=upper_lim }
-    }
-
-    fn write_string(s: &mut String, ctx: Self::WriteCtx) {
-        write!(s, "{ctx:e}").unwrap();
-    }
-}
-
-impl<F: Float> Iterator for SubnormComplete<F>
-where
-    RangeInclusive<F::Int>: Iterator<Item = F::Int>,
-{
-    type Item = F;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        Some(F::from_bits(self.iter.next()?))
-    }
-}
diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs
deleted file mode 100644
index f590149523b..00000000000
--- a/src/etc/test-float-parse/src/lib.rs
+++ /dev/null
@@ -1,419 +0,0 @@
-#![feature(f16)]
-#![feature(cfg_target_has_reliable_f16_f128)]
-#![expect(internal_features)] // reliable_f16_f128
-
-mod traits;
-mod ui;
-mod validate;
-
-use std::any::type_name;
-use std::cmp::min;
-use std::ops::RangeInclusive;
-use std::process::ExitCode;
-use std::sync::OnceLock;
-use std::sync::atomic::{AtomicU64, Ordering};
-use std::{fmt, time};
-
-use rand::distr::{Distribution, StandardUniform};
-use rayon::prelude::*;
-use time::{Duration, Instant};
-use traits::{Float, Generator, Int};
-use validate::CheckError;
-
-/// Test generators.
-mod gen_ {
-    pub mod exhaustive;
-    pub mod exponents;
-    pub mod fuzz;
-    pub mod integers;
-    pub mod long_fractions;
-    pub mod many_digits;
-    pub mod sparse;
-    pub mod spot_checks;
-    pub mod subnorm;
-}
-
-/// How many failures to exit after if unspecified.
-const DEFAULT_MAX_FAILURES: u64 = 20;
-
-/// Register exhaustive tests only for <= 32 bits. No more because it would take years.
-const MAX_BITS_FOR_EXHAUUSTIVE: u32 = 32;
-
-/// If there are more tests than this threshold, the test will be deferred until after all
-/// others run (so as to avoid thread pool starvation). They also can be excluded with
-/// `--skip-huge`.
-const HUGE_TEST_CUTOFF: u64 = 5_000_000;
-
-/// Seed for tests that use a deterministic RNG.
-const SEED: [u8; 32] = *b"3.141592653589793238462643383279";
-
-/// Global configuration.
-#[derive(Debug)]
-pub struct Config {
-    pub timeout: Duration,
-    /// Failures per test
-    pub max_failures: u64,
-    pub disable_max_failures: bool,
-    /// If `None`, the default will be used
-    pub fuzz_count: Option<u64>,
-    pub skip_huge: bool,
-}
-
-impl Default for Config {
-    fn default() -> Self {
-        Self {
-            timeout: Duration::from_secs(60 * 60 * 3),
-            max_failures: DEFAULT_MAX_FAILURES,
-            disable_max_failures: false,
-            fuzz_count: None,
-            skip_huge: false,
-        }
-    }
-}
-
-/// Collect, filter, and launch all tests.
-pub fn run(cfg: Config, include: &[String], exclude: &[String]) -> ExitCode {
-    // With default parallelism, the CPU doesn't saturate. We don't need to be nice to
-    // other processes, so do 1.5x to make sure we use all available resources.
-    let threads = std::thread::available_parallelism().map(Into::into).unwrap_or(0) * 3 / 2;
-    rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
-
-    let mut tests = register_tests(&cfg);
-    println!("registered");
-    let initial_tests: Vec<_> = tests.iter().map(|t| t.name.clone()).collect();
-
-    let unmatched: Vec<_> = include
-        .iter()
-        .chain(exclude.iter())
-        .filter(|filt| !tests.iter().any(|t| t.matches(filt)))
-        .collect();
-
-    assert!(
-        unmatched.is_empty(),
-        "filters were provided that have no matching tests: {unmatched:#?}"
-    );
-
-    tests.retain(|test| !exclude.iter().any(|exc| test.matches(exc)));
-
-    if cfg.skip_huge {
-        tests.retain(|test| !test.is_huge_test());
-    }
-
-    if !include.is_empty() {
-        tests.retain(|test| include.iter().any(|inc| test.matches(inc)));
-    }
-
-    for exc in initial_tests.iter().filter(|orig_name| !tests.iter().any(|t| t.name == **orig_name))
-    {
-        println!("Skipping test '{exc}'");
-    }
-
-    println!("Launching all");
-    let elapsed = launch_tests(&mut tests, &cfg);
-    ui::finish_all(&tests, elapsed, &cfg)
-}
-
-/// Enumerate tests to run but don't actually run them.
-pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
-    let mut tests = Vec::new();
-
-    // Register normal generators for all floats.
-
-    #[cfg(not(bootstrap))]
-    #[cfg(target_has_reliable_f16)]
-    register_float::<f16>(&mut tests, cfg);
-    register_float::<f32>(&mut tests, cfg);
-    register_float::<f64>(&mut tests, cfg);
-
-    tests.sort_unstable_by_key(|t| (t.float_name, t.gen_name));
-    for i in 0..(tests.len() - 1) {
-        if tests[i].gen_name == tests[i + 1].gen_name {
-            panic!("duplicate test name {}", tests[i].gen_name);
-        }
-    }
-
-    tests
-}
-
-/// Register all generators for a single float.
-fn register_float<F: Float>(tests: &mut Vec<TestInfo>, cfg: &Config)
-where
-    RangeInclusive<F::Int>: Iterator<Item = F::Int>,
-    <F::Int as TryFrom<u128>>::Error: std::fmt::Debug,
-    StandardUniform: Distribution<<F as traits::Float>::Int>,
-{
-    if F::BITS <= MAX_BITS_FOR_EXHAUUSTIVE {
-        // Only run exhaustive tests if there is a chance of completion.
-        TestInfo::register::<F, gen_::exhaustive::Exhaustive<F>>(tests);
-    }
-
-    gen_::fuzz::Fuzz::<F>::set_iterations(cfg.fuzz_count);
-
-    TestInfo::register::<F, gen_::exponents::LargeExponents<F>>(tests);
-    TestInfo::register::<F, gen_::exponents::SmallExponents<F>>(tests);
-    TestInfo::register::<F, gen_::fuzz::Fuzz<F>>(tests);
-    TestInfo::register::<F, gen_::integers::LargeInt<F>>(tests);
-    TestInfo::register::<F, gen_::integers::SmallInt>(tests);
-    TestInfo::register::<F, gen_::long_fractions::RepeatingDecimal>(tests);
-    TestInfo::register::<F, gen_::many_digits::RandDigits<F>>(tests);
-    TestInfo::register::<F, gen_::sparse::FewOnesFloat<F>>(tests);
-    TestInfo::register::<F, gen_::sparse::FewOnesInt<F>>(tests);
-    TestInfo::register::<F, gen_::spot_checks::RegressionCheck>(tests);
-    TestInfo::register::<F, gen_::spot_checks::Special>(tests);
-    TestInfo::register::<F, gen_::subnorm::SubnormComplete<F>>(tests);
-    TestInfo::register::<F, gen_::subnorm::SubnormEdgeCases<F>>(tests);
-}
-
-/// Configuration for a single test.
-#[derive(Debug)]
-pub struct TestInfo {
-    pub name: String,
-    float_name: &'static str,
-    float_bits: u32,
-    gen_name: &'static str,
-    /// Name for display in the progress bar.
-    short_name: String,
-    /// Pad the short name to a common width for progress bar use.
-    short_name_padded: String,
-    total_tests: u64,
-    /// Function to launch this test.
-    launch: fn(&TestInfo, &Config),
-    /// Progress bar to be updated.
-    progress: Option<ui::Progress>,
-    /// Once completed, this will be set.
-    completed: OnceLock<Completed>,
-}
-
-impl TestInfo {
-    /// Check if either the name or short name is a match, for filtering.
-    fn matches(&self, pat: &str) -> bool {
-        self.short_name.contains(pat) || self.name.contains(pat)
-    }
-
-    /// Create a `TestInfo` for a given float and generator, then add it to a list.
-    fn register<F: Float, G: Generator<F>>(v: &mut Vec<Self>) {
-        let f_name = type_name::<F>();
-        let gen_name = G::NAME;
-        let gen_short_name = G::SHORT_NAME;
-        let name = format!("{f_name} {gen_name}");
-        let short_name = format!("{f_name} {gen_short_name}");
-        let short_name_padded = format!("{short_name:18}");
-
-        let info = TestInfo {
-            float_name: f_name,
-            float_bits: F::BITS,
-            gen_name,
-            progress: None,
-            name,
-            short_name_padded,
-            short_name,
-            launch: test_runner::<F, G>,
-            total_tests: G::total_tests(),
-            completed: OnceLock::new(),
-        };
-        v.push(info);
-    }
-
-    /// True if this should be run after all others.
-    fn is_huge_test(&self) -> bool {
-        self.total_tests >= HUGE_TEST_CUTOFF
-    }
-
-    /// When the test is finished, update progress bar messages and finalize.
-    fn complete(&self, c: Completed) {
-        self.progress.as_ref().unwrap().complete(&c, 0);
-        self.completed.set(c).unwrap();
-    }
-}
-
-/// Result of an input did not parsing successfully.
-#[derive(Clone, Debug)]
-enum CheckFailure {
-    /// Above the zero cutoff but got rounded to zero.
-    UnexpectedZero,
-    /// Below the infinity cutoff but got rounded to infinity.
-    UnexpectedInf,
-    /// Above the negative infinity cutoff but got rounded to negative infinity.
-    UnexpectedNegInf,
-    /// Got a `NaN` when none was expected.
-    UnexpectedNan,
-    /// Expected `NaN`, got none.
-    ExpectedNan,
-    /// Expected infinity, got finite.
-    ExpectedInf,
-    /// Expected negative infinity, got finite.
-    ExpectedNegInf,
-    /// The value exceeded its error tolerance.
-    InvalidReal {
-        /// Error from the expected value, as a float.
-        error_float: Option<f64>,
-        /// Error as a rational string (since it can't always be represented as a float).
-        error_str: Box<str>,
-        /// True if the error was caused by not rounding to even at the midpoint between
-        /// two representable values.
-        incorrect_midpoint_rounding: bool,
-    },
-    /// String did not parse successfully.
-    ParsingFailed(Box<str>),
-    /// A panic was caught.
-    Panic(Box<str>),
-}
-
-impl fmt::Display for CheckFailure {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            CheckFailure::UnexpectedZero => {
-                write!(f, "incorrectly rounded to 0 (expected nonzero)")
-            }
-            CheckFailure::UnexpectedInf => {
-                write!(f, "incorrectly rounded to +inf (expected finite)")
-            }
-            CheckFailure::UnexpectedNegInf => {
-                write!(f, "incorrectly rounded to -inf (expected finite)")
-            }
-            CheckFailure::UnexpectedNan => write!(f, "got a NaN where none was expected"),
-            CheckFailure::ExpectedNan => write!(f, "expected a NaN but did not get it"),
-            CheckFailure::ExpectedInf => write!(f, "expected +inf but did not get it"),
-            CheckFailure::ExpectedNegInf => write!(f, "expected -inf but did not get it"),
-            CheckFailure::InvalidReal { error_float, error_str, incorrect_midpoint_rounding } => {
-                if *incorrect_midpoint_rounding {
-                    write!(
-                        f,
-                        "midpoint between two representable values did not correctly \
-                        round to even; error: {error_str}"
-                    )?;
-                } else {
-                    write!(f, "real number did not parse correctly; error: {error_str}")?;
-                }
-
-                if let Some(float) = error_float {
-                    write!(f, " ({float})")?;
-                }
-                Ok(())
-            }
-            CheckFailure::ParsingFailed(e) => write!(f, "parsing failed: {e}"),
-            CheckFailure::Panic(e) => write!(f, "function panicked: {e}"),
-        }
-    }
-}
-
-/// Information about a completed test generator.
-#[derive(Clone, Debug)]
-struct Completed {
-    /// Finished tests (both successful and failed).
-    executed: u64,
-    /// Failed tests.
-    failures: u64,
-    /// Extra exit information if unsuccessful.
-    result: Result<FinishedAll, EarlyExit>,
-    /// If there is something to warn about (e.g bad estimate), leave it here.
-    warning: Option<Box<str>>,
-    /// Total time to run the test.
-    elapsed: Duration,
-}
-
-/// Marker for completing all tests (used in `Result` types).
-#[derive(Clone, Debug)]
-struct FinishedAll;
-
-/// Reasons for exiting early.
-#[derive(Clone, Debug)]
-enum EarlyExit {
-    Timeout,
-    MaxFailures,
-}
-
-/// Run all tests in `tests`.
-///
-/// This launches a main thread that receives messages and handlees UI updates, and uses the
-/// rest of the thread pool to execute the tests.
-fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration {
-    // Run shorter tests and smaller float types first.
-    tests.sort_unstable_by_key(|test| (test.total_tests, test.float_bits));
-
-    for test in tests.iter() {
-        println!("Launching test '{}'", test.name);
-    }
-
-    let mut all_progress_bars = Vec::new();
-    let start = Instant::now();
-
-    for test in tests.iter_mut() {
-        test.progress = Some(ui::Progress::new(test, &mut all_progress_bars));
-        ui::set_panic_hook(&all_progress_bars);
-        ((test.launch)(test, cfg));
-    }
-
-    start.elapsed()
-}
-
-/// Test runer for a single generator.
-///
-/// This calls the generator's iterator multiple times (in parallel) and validates each output.
-fn test_runner<F: Float, G: Generator<F>>(test: &TestInfo, cfg: &Config) {
-    let gen_ = G::new();
-    let executed = AtomicU64::new(0);
-    let failures = AtomicU64::new(0);
-
-    let checks_per_update = min(test.total_tests, 1000);
-    let started = Instant::now();
-
-    // Function to execute for a single test iteration.
-    let check_one = |buf: &mut String, ctx: G::WriteCtx| {
-        let executed = executed.fetch_add(1, Ordering::Relaxed);
-        buf.clear();
-        G::write_string(buf, ctx);
-
-        match validate::validate::<F>(buf) {
-            Ok(()) => (),
-            Err(e) => {
-                let CheckError { fail, input, float_res } = e;
-                test.progress.as_ref().unwrap().println(&format!(
-                    "Failure in '{}': {fail}. parsing '{input}'. Parsed as: {float_res}",
-                    test.name
-                ));
-
-                let f = failures.fetch_add(1, Ordering::Relaxed);
-                // End early if the limit is exceeded.
-                if f >= cfg.max_failures {
-                    return Err(EarlyExit::MaxFailures);
-                }
-            }
-        };
-
-        // Send periodic updates
-        if executed % checks_per_update == 0 {
-            let failures = failures.load(Ordering::Relaxed);
-            test.progress.as_ref().unwrap().update(executed, failures);
-            if started.elapsed() > cfg.timeout {
-                return Err(EarlyExit::Timeout);
-            }
-        }
-
-        Ok(())
-    };
-
-    // Run the test iterations in parallel. Each thread gets a string buffer to write
-    // its check values to.
-    let res = gen_.par_bridge().try_for_each_init(String::new, check_one);
-
-    let elapsed = started.elapsed();
-    let executed = executed.into_inner();
-    let failures = failures.into_inner();
-
-    // Warn about bad estimates if relevant.
-    let warning = if executed != test.total_tests && res.is_ok() {
-        let msg = format!(
-            "executed tests != estimated ({executed} != {}) for {}",
-            test.total_tests,
-            G::NAME
-        );
-
-        Some(msg.into())
-    } else {
-        None
-    };
-
-    let result = res.map(|()| FinishedAll);
-    test.complete(Completed { executed, failures, result, warning, elapsed });
-}
diff --git a/src/etc/test-float-parse/src/main.rs b/src/etc/test-float-parse/src/main.rs
deleted file mode 100644
index 9c6cad7324f..00000000000
--- a/src/etc/test-float-parse/src/main.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use std::process::ExitCode;
-use std::time::Duration;
-
-use test_float_parse as tfp;
-
-static HELP: &str = r#"Usage:
-
-  ./test-float-parse [--timeout x] [--exclude x] [--max-failures x] [INCLUDE ...]
-  ./test-float-parse [--fuzz-count x] [INCLUDE ...]
-  ./test-float-parse [--skip-huge] [INCLUDE ...]
-  ./test-float-parse --list
-
-Args:
-
-  INCLUDE                  Include only tests with names containing these
-                           strings. If this argument is not specified, all tests
-                           are run.
-  --timeout N              Exit after this amount of time (in seconds).
-  --exclude FILTER         Skip tests containing this string. May be specified
-                           more than once.
-  --list                   List available tests.
-  --max-failures N         Limit to N failures per test. Defaults to 20. Pass
-                           "--max-failures none" to remove this limit.
-  --fuzz-count N           Run the fuzzer with N iterations. Only has an effect
-                           if fuzz tests are enabled. Pass `--fuzz-count none`
-                           to remove this limit.
-  --skip-huge              Skip tests that run for a long time.
-  --all                    Reset previous `--exclude`, `--skip-huge`, and
-                           `INCLUDE` arguments (useful for running all tests
-                           via `./x`).
-"#;
-
-enum ArgMode {
-    Any,
-    Timeout,
-    Exclude,
-    FuzzCount,
-    MaxFailures,
-}
-
-fn main() -> ExitCode {
-    if cfg!(debug_assertions) {
-        println!(
-            "WARNING: running in debug mode. Release mode is recommended to reduce test duration."
-        );
-        std::thread::sleep(Duration::from_secs(2));
-    }
-
-    let args: Vec<_> = std::env::args().skip(1).collect();
-    if args.iter().any(|arg| arg == "--help" || arg == "-h") {
-        println!("{HELP}");
-        return ExitCode::SUCCESS;
-    }
-
-    if args.iter().any(|arg| arg == "--list") {
-        let tests = tfp::register_tests(&tfp::Config::default());
-        println!("Available tests:");
-        for t in tests {
-            println!("{}", t.name);
-        }
-
-        return ExitCode::SUCCESS;
-    }
-
-    let (cfg, include, exclude) = parse_args(args);
-
-    tfp::run(cfg, &include, &exclude)
-}
-
-/// Simple command argument parser
-fn parse_args(args: Vec<String>) -> (tfp::Config, Vec<String>, Vec<String>) {
-    let mut cfg = tfp::Config::default();
-
-    let mut mode = ArgMode::Any;
-    let mut include = Vec::new();
-    let mut exclude = Vec::new();
-
-    for arg in args {
-        mode = match mode {
-            ArgMode::Any if arg == "--timeout" => ArgMode::Timeout,
-            ArgMode::Any if arg == "--exclude" => ArgMode::Exclude,
-            ArgMode::Any if arg == "--max-failures" => ArgMode::MaxFailures,
-            ArgMode::Any if arg == "--fuzz-count" => ArgMode::FuzzCount,
-            ArgMode::Any if arg == "--skip-huge" => {
-                cfg.skip_huge = true;
-                ArgMode::Any
-            }
-            ArgMode::Any if arg == "--all" => {
-                cfg.skip_huge = false;
-                include.clear();
-                exclude.clear();
-                ArgMode::Any
-            }
-            ArgMode::Any if arg.starts_with('-') => {
-                panic!("Unknown argument {arg}. Usage:\n{HELP}")
-            }
-            ArgMode::Any => {
-                include.push(arg);
-                ArgMode::Any
-            }
-            ArgMode::Timeout => {
-                cfg.timeout = Duration::from_secs(arg.parse().unwrap());
-                ArgMode::Any
-            }
-            ArgMode::MaxFailures => {
-                if arg == "none" {
-                    cfg.disable_max_failures = true;
-                } else {
-                    cfg.max_failures = arg.parse().unwrap();
-                }
-                ArgMode::Any
-            }
-            ArgMode::FuzzCount => {
-                if arg == "none" {
-                    cfg.fuzz_count = Some(u64::MAX);
-                } else {
-                    cfg.fuzz_count = Some(arg.parse().unwrap());
-                }
-                ArgMode::Any
-            }
-            ArgMode::Exclude => {
-                exclude.push(arg);
-                ArgMode::Any
-            }
-        }
-    }
-
-    (cfg, include, exclude)
-}
diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs
deleted file mode 100644
index 16484f8fe2c..00000000000
--- a/src/etc/test-float-parse/src/traits.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-//! Interfaces used throughout this crate.
-
-use std::str::FromStr;
-use std::{fmt, ops};
-
-use num::Integer;
-use num::bigint::ToBigInt;
-
-use crate::validate::Constants;
-
-/// Integer types.
-#[allow(dead_code)] // Some functions only used for testing
-pub trait Int:
-    Clone
-    + Copy
-    + fmt::Debug
-    + fmt::Display
-    + fmt::LowerHex
-    + ops::Add<Output = Self>
-    + ops::Sub<Output = Self>
-    + ops::Shl<u32, Output = Self>
-    + ops::Shr<u32, Output = Self>
-    + ops::BitAnd<Output = Self>
-    + ops::BitOr<Output = Self>
-    + ops::Not<Output = Self>
-    + ops::AddAssign
-    + ops::BitAndAssign
-    + ops::BitOrAssign
-    + From<u8>
-    + TryFrom<i8>
-    + TryFrom<u32, Error: fmt::Debug>
-    + TryFrom<u64, Error: fmt::Debug>
-    + TryFrom<u128, Error: fmt::Debug>
-    + TryInto<u64, Error: fmt::Debug>
-    + TryInto<u32, Error: fmt::Debug>
-    + ToBigInt
-    + PartialOrd
-    + Integer
-    + Send
-    + 'static
-{
-    type Signed: Int;
-    type Bytes: Default + AsMut<[u8]>;
-
-    const BITS: u32;
-    const ZERO: Self;
-    const ONE: Self;
-    const MAX: Self;
-
-    fn to_signed(self) -> Self::Signed;
-    fn wrapping_neg(self) -> Self;
-    fn trailing_zeros(self) -> u32;
-
-    fn hex(self) -> String {
-        format!("{self:x}")
-    }
-}
-
-macro_rules! impl_int {
-    ($($uty:ty, $sty:ty);+) => {
-        $(
-            impl Int for $uty {
-                type Signed = $sty;
-                type Bytes = [u8; Self::BITS as usize / 8];
-                const BITS: u32 = Self::BITS;
-                const ZERO: Self = 0;
-                const ONE: Self = 1;
-                const MAX: Self = Self::MAX;
-                fn to_signed(self) -> Self::Signed {
-                    self.try_into().unwrap()
-                }
-                fn wrapping_neg(self) -> Self {
-                    self.wrapping_neg()
-                }
-                fn trailing_zeros(self) -> u32 {
-                    self.trailing_zeros()
-                }
-            }
-
-            impl Int for $sty {
-                type Signed = Self;
-                type Bytes = [u8; Self::BITS as usize / 8];
-                const BITS: u32 = Self::BITS;
-                const ZERO: Self = 0;
-                const ONE: Self = 1;
-                const MAX: Self = Self::MAX;
-                fn to_signed(self) -> Self::Signed {
-                    self
-                }
-                fn wrapping_neg(self) -> Self {
-                    self.wrapping_neg()
-                }
-                fn trailing_zeros(self) -> u32 {
-                    self.trailing_zeros()
-                }
-            }
-        )+
-    }
-}
-
-impl_int!(u16, i16; u32, i32; u64, i64);
-
-/// Floating point types.
-pub trait Float:
-    Copy + fmt::Debug + fmt::LowerExp + FromStr<Err: fmt::Display> + Sized + Send + 'static
-{
-    /// Unsigned integer of same width
-    type Int: Int<Signed = Self::SInt>;
-    type SInt: Int;
-
-    /// Total bits
-    const BITS: u32;
-
-    /// (Stored) bits in the mantissa)
-    const MAN_BITS: u32;
-
-    /// Bits in the exponent
-    const EXP_BITS: u32 = Self::BITS - Self::MAN_BITS - 1;
-
-    /// A saturated exponent (all ones)
-    const EXP_SAT: u32 = (1 << Self::EXP_BITS) - 1;
-
-    /// The exponent bias, also its maximum value
-    const EXP_BIAS: u32 = Self::EXP_SAT >> 1;
-
-    const MAN_MASK: Self::Int;
-    const SIGN_MASK: Self::Int;
-
-    fn from_bits(i: Self::Int) -> Self;
-    fn to_bits(self) -> Self::Int;
-
-    /// Rational constants associated with this float type.
-    fn constants() -> &'static Constants;
-
-    fn is_sign_negative(self) -> bool {
-        (self.to_bits() & Self::SIGN_MASK) > Self::Int::ZERO
-    }
-
-    /// Exponent without adjustment for bias.
-    fn exponent(self) -> u32 {
-        ((self.to_bits() >> Self::MAN_BITS) & Self::EXP_SAT.try_into().unwrap()).try_into().unwrap()
-    }
-
-    fn mantissa(self) -> Self::Int {
-        self.to_bits() & Self::MAN_MASK
-    }
-}
-
-macro_rules! impl_float {
-    ($($fty:ty, $ity:ty);+) => {
-        $(
-            impl Float for $fty {
-                type Int = $ity;
-                type SInt = <Self::Int as Int>::Signed;
-                const BITS: u32 = <$ity>::BITS;
-                const MAN_BITS: u32 = Self::MANTISSA_DIGITS - 1;
-                const MAN_MASK: Self::Int = (Self::Int::ONE << Self::MAN_BITS) - Self::Int::ONE;
-                const SIGN_MASK: Self::Int = Self::Int::ONE << (Self::BITS-1);
-                fn from_bits(i: Self::Int) -> Self { Self::from_bits(i) }
-                fn to_bits(self) -> Self::Int { self.to_bits() }
-                fn constants() -> &'static Constants {
-                    use std::sync::LazyLock;
-                    static CONSTANTS: LazyLock<Constants> = LazyLock::new(Constants::new::<$fty>);
-                    &CONSTANTS
-                }
-            }
-        )+
-    }
-}
-
-impl_float!(f32, u32; f64, u64);
-
-#[cfg(not(bootstrap))]
-#[cfg(target_has_reliable_f16)]
-impl_float!(f16, u16);
-
-/// A test generator. Should provide an iterator that produces unique patterns to parse.
-///
-/// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to
-/// write the string at a later step. This is done separately so that we can reuse string
-/// allocations (which otherwise turn out to be a pretty expensive part of these tests).
-pub trait Generator<F: Float>: Iterator<Item = Self::WriteCtx> + Send + 'static {
-    /// Full display and filtering name
-    const NAME: &'static str = Self::SHORT_NAME;
-
-    /// Name for display with the progress bar
-    const SHORT_NAME: &'static str;
-
-    /// The context needed to create a test string.
-    type WriteCtx: Send;
-
-    /// Number of tests that will be run.
-    fn total_tests() -> u64;
-
-    /// Constructor for this test generator.
-    fn new() -> Self;
-
-    /// Create a test string given write context, which was produced as a step from the iterator.
-    ///
-    /// `s` will be provided empty.
-    fn write_string(s: &mut String, ctx: Self::WriteCtx);
-}
-
-/// For tests that use iterator combinators, it is easier to just to box the iterator than trying
-/// to specify its type. This is a shorthand for the usual type.
-pub type BoxGenIter<This, F> = Box<dyn Iterator<Item = <This as Generator<F>>::WriteCtx> + Send>;
diff --git a/src/etc/test-float-parse/src/ui.rs b/src/etc/test-float-parse/src/ui.rs
deleted file mode 100644
index 73473eef0bf..00000000000
--- a/src/etc/test-float-parse/src/ui.rs
+++ /dev/null
@@ -1,168 +0,0 @@
-//! Progress bars and such.
-
-use std::any::type_name;
-use std::fmt;
-use std::io::{self, Write};
-use std::process::ExitCode;
-use std::time::Duration;
-
-use indicatif::{ProgressBar, ProgressStyle};
-
-use crate::{Completed, Config, EarlyExit, FinishedAll, TestInfo};
-
-/// Templates for progress bars.
-const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \
-        {human_pos:>8}/{human_len:8} {msg} f {per_sec:14} eta {eta:8}";
-const PB_TEMPLATE_FINAL: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \
-        {human_pos:>8}/{human_len:8} {msg:.COLOR} {per_sec:18} {elapsed_precise}";
-
-/// Thin abstraction over our usage of a `ProgressBar`.
-#[derive(Debug)]
-pub struct Progress {
-    pb: ProgressBar,
-    make_final_style: NoDebug<Box<dyn Fn(&'static str) -> ProgressStyle + Sync>>,
-}
-
-impl Progress {
-    /// Create a new progress bar within a multiprogress bar.
-    pub fn new(test: &TestInfo, all_bars: &mut Vec<ProgressBar>) -> Self {
-        let initial_template = PB_TEMPLATE.replace("NAME", &test.short_name_padded);
-        let final_template = PB_TEMPLATE_FINAL.replace("NAME", &test.short_name_padded);
-        let initial_style =
-            ProgressStyle::with_template(&initial_template).unwrap().progress_chars("##-");
-        let make_final_style = move |color| {
-            ProgressStyle::with_template(&final_template.replace("COLOR", color))
-                .unwrap()
-                .progress_chars("##-")
-        };
-
-        let pb = ProgressBar::new(test.total_tests);
-        pb.set_style(initial_style);
-        pb.set_length(test.total_tests);
-        pb.set_message("0");
-        all_bars.push(pb.clone());
-
-        Progress { pb, make_final_style: NoDebug(Box::new(make_final_style)) }
-    }
-
-    /// Completed a out of b tests.
-    pub fn update(&self, completed: u64, failures: u64) {
-        // Infrequently update the progress bar.
-        if completed % 5_000 == 0 || failures > 0 {
-            self.pb.set_position(completed);
-        }
-
-        if failures > 0 {
-            self.pb.set_message(format! {"{failures}"});
-        }
-    }
-
-    /// Finalize the progress bar.
-    pub fn complete(&self, c: &Completed, real_total: u64) {
-        let f = c.failures;
-        let (color, msg, finish_fn): (&str, String, fn(&ProgressBar)) = match &c.result {
-            Ok(FinishedAll) if f > 0 => {
-                ("red", format!("{f} f (completed with errors)",), ProgressBar::finish)
-            }
-            Ok(FinishedAll) => {
-                ("green", format!("{f} f (completed successfully)",), ProgressBar::finish)
-            }
-            Err(EarlyExit::Timeout) => ("red", format!("{f} f (timed out)"), ProgressBar::abandon),
-            Err(EarlyExit::MaxFailures) => {
-                ("red", format!("{f} f (failure limit)"), ProgressBar::abandon)
-            }
-        };
-
-        self.pb.set_position(real_total);
-        self.pb.set_style(self.make_final_style.0(color));
-        self.pb.set_message(msg);
-        finish_fn(&self.pb);
-    }
-
-    /// Print a message to stdout above the current progress bar.
-    pub fn println(&self, msg: &str) {
-        self.pb.suspend(|| println!("{msg}"));
-    }
-}
-
-/// Print final messages after all tests are complete.
-pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) -> ExitCode {
-    println!("\n\nResults:");
-
-    let mut failed_generators = 0;
-    let mut stopped_generators = 0;
-
-    for t in tests {
-        let Completed { executed, failures, elapsed, warning, result } = t.completed.get().unwrap();
-
-        let stat = if result.is_err() {
-            stopped_generators += 1;
-            "STOPPED"
-        } else if *failures > 0 {
-            failed_generators += 1;
-            "FAILURE"
-        } else {
-            "SUCCESS"
-        };
-
-        println!(
-            "    {stat} for generator '{name}'. {passed}/{executed} passed in {elapsed:?}",
-            name = t.name,
-            passed = executed - failures,
-        );
-
-        if let Some(warning) = warning {
-            println!("      warning: {warning}");
-        }
-
-        match result {
-            Ok(FinishedAll) => (),
-            Err(EarlyExit::Timeout) => {
-                println!("      exited early; exceded {:?} timeout", cfg.timeout)
-            }
-            Err(EarlyExit::MaxFailures) => {
-                println!("      exited early; exceeded {:?} max failures", cfg.max_failures)
-            }
-        }
-    }
-
-    println!(
-        "{passed}/{} tests succeeded in {total_elapsed:?} ({passed} passed, {} failed, {} stopped)",
-        tests.len(),
-        failed_generators,
-        stopped_generators,
-        passed = tests.len() - failed_generators - stopped_generators,
-    );
-
-    if failed_generators > 0 || stopped_generators > 0 {
-        ExitCode::FAILURE
-    } else {
-        ExitCode::SUCCESS
-    }
-}
-
-/// indicatif likes to eat panic messages. This workaround isn't ideal, but it improves things.
-/// <https://github.com/console-rs/indicatif/issues/121>.
-pub fn set_panic_hook(drop_bars: &[ProgressBar]) {
-    let hook = std::panic::take_hook();
-    let drop_bars = drop_bars.to_owned();
-    std::panic::set_hook(Box::new(move |info| {
-        for bar in &drop_bars {
-            bar.abandon();
-            println!();
-            io::stdout().flush().unwrap();
-            io::stderr().flush().unwrap();
-        }
-        hook(info);
-    }));
-}
-
-/// Allow non-Debug items in a `derive(Debug)` struct.
-#[derive(Clone)]
-struct NoDebug<T>(T);
-
-impl<T> fmt::Debug for NoDebug<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(type_name::<Self>())
-    }
-}
diff --git a/src/etc/test-float-parse/src/validate.rs b/src/etc/test-float-parse/src/validate.rs
deleted file mode 100644
index 40dda274e3b..00000000000
--- a/src/etc/test-float-parse/src/validate.rs
+++ /dev/null
@@ -1,394 +0,0 @@
-//! Everything related to verifying that parsed outputs are correct.
-
-use std::any::{Any, type_name};
-use std::collections::BTreeMap;
-use std::ops::RangeInclusive;
-use std::str::FromStr;
-use std::sync::LazyLock;
-
-use num::bigint::ToBigInt;
-use num::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive};
-
-use crate::{CheckFailure, Float, Int};
-
-/// Powers of two that we store for constants. Account for binary128 which has a 15-bit exponent.
-const POWERS_OF_TWO_RANGE: RangeInclusive<i32> = (-(2 << 15))..=(2 << 15);
-
-/// Powers of ten that we cache. Account for binary128, which can fit +4932/-4931
-const POWERS_OF_TEN_RANGE: RangeInclusive<i32> = -5_000..=5_000;
-
-/// Cached powers of 10 so we can look them up rather than recreating.
-static POWERS_OF_TEN: LazyLock<BTreeMap<i32, BigRational>> = LazyLock::new(|| {
-    POWERS_OF_TEN_RANGE.map(|exp| (exp, BigRational::from_u32(10).unwrap().pow(exp))).collect()
-});
-
-/// Rational property-related constants for a specific float type.
-#[allow(dead_code)]
-#[derive(Debug)]
-pub struct Constants {
-    /// The minimum positive value (a subnormal).
-    min_subnormal: BigRational,
-    /// The maximum possible finite value.
-    max: BigRational,
-    /// Cutoff between rounding to zero and rounding to the minimum value (min subnormal).
-    zero_cutoff: BigRational,
-    /// Cutoff between rounding to the max value and rounding to infinity.
-    inf_cutoff: BigRational,
-    /// Opposite of `inf_cutoff`
-    neg_inf_cutoff: BigRational,
-    /// The powers of two for all relevant integers.
-    powers_of_two: BTreeMap<i32, BigRational>,
-    /// Half of each power of two. ULP = "unit in last position".
-    ///
-    /// This is a mapping from integers to half the precision available at that exponent. In other
-    /// words, `0.5 * 2^n` = `2^(n-1)`, which is half the distance between `m * 2^n` and
-    /// `(m + 1) * 2^n`, m ∈ ℤ.
-    ///
-    /// So, this is the maximum error from a real number to its floating point representation,
-    /// assuming the float type can represent the exponent.
-    half_ulp: BTreeMap<i32, BigRational>,
-    /// Handy to have around so we don't need to reallocate for it
-    two: BigInt,
-}
-
-impl Constants {
-    pub fn new<F: Float>() -> Self {
-        let two_int = &BigInt::from_u32(2).unwrap();
-        let two = &BigRational::from_integer(2.into());
-
-        // The minimum subnormal (aka minimum positive) value. Most negative power of two is the
-        // minimum exponent (bias - 1) plus the extra from shifting within the mantissa bits.
-        let min_subnormal = two.pow(-(F::EXP_BIAS + F::MAN_BITS - 1).to_signed());
-
-        // The maximum value is the maximum exponent with a fully saturated mantissa. This
-        // is easiest to calculate by evaluating what the next value up would be if representable
-        // (zeroed mantissa, exponent increments by one, i.e. `2^(bias + 1)`), and subtracting
-        // a single LSB (`2 ^ (-mantissa_bits)`).
-        let max = (two - two.pow(-F::MAN_BITS.to_signed())) * (two.pow(F::EXP_BIAS.to_signed()));
-        let zero_cutoff = &min_subnormal / two_int;
-
-        let inf_cutoff = &max + two_int.pow(F::EXP_BIAS - F::MAN_BITS - 1);
-        let neg_inf_cutoff = -&inf_cutoff;
-
-        let powers_of_two: BTreeMap<i32, _> =
-            (POWERS_OF_TWO_RANGE).map(|n| (n, two.pow(n))).collect();
-        let mut half_ulp = powers_of_two.clone();
-        half_ulp.iter_mut().for_each(|(_k, v)| *v = &*v / two_int);
-
-        Self {
-            min_subnormal,
-            max,
-            zero_cutoff,
-            inf_cutoff,
-            neg_inf_cutoff,
-            powers_of_two,
-            half_ulp,
-            two: two_int.clone(),
-        }
-    }
-}
-
-/// Validate that a string parses correctly
-pub fn validate<F: Float>(input: &str) -> Result<(), CheckError> {
-    // Catch panics in case debug assertions within `std` fail.
-    let parsed = std::panic::catch_unwind(|| {
-        input.parse::<F>().map_err(|e| CheckError {
-            fail: CheckFailure::ParsingFailed(e.to_string().into()),
-            input: input.into(),
-            float_res: "none".into(),
-        })
-    })
-    .map_err(|e| convert_panic_error(&e, input))??;
-
-    // Parsed float, decoded into significand and exponent
-    let decoded = decode(parsed);
-
-    // Float parsed separately into a rational
-    let rational = Rational::parse(input);
-
-    // Verify that the values match
-    decoded.check(rational, input)
-}
-
-/// Turn panics into concrete error types.
-fn convert_panic_error(e: &dyn Any, input: &str) -> CheckError {
-    let msg = e
-        .downcast_ref::<String>()
-        .map(|s| s.as_str())
-        .or_else(|| e.downcast_ref::<&str>().copied())
-        .unwrap_or("(no contents)");
-
-    CheckError {
-        fail: CheckFailure::Panic(msg.into()),
-        input: input.into(),
-        float_res: "none".into(),
-    }
-}
-
-/// The result of parsing a string to a float type.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum FloatRes<F: Float> {
-    Inf,
-    NegInf,
-    Zero,
-    Nan,
-    /// A real number with significand and exponent. Value is `sig * 2 ^ exp`.
-    Real {
-        sig: F::SInt,
-        exp: i32,
-    },
-}
-
-#[derive(Clone, Debug)]
-pub struct CheckError {
-    pub fail: CheckFailure,
-    /// String for which parsing was attempted.
-    pub input: Box<str>,
-    /// The parsed & decomposed `FloatRes`, already stringified so we don't need generics here.
-    pub float_res: Box<str>,
-}
-
-impl<F: Float> FloatRes<F> {
-    /// Given a known exact rational, check that this representation is accurate within the
-    /// limits of the float representation. If not, construct a failure `Update` to send.
-    fn check(self, expected: Rational, input: &str) -> Result<(), CheckError> {
-        let consts = F::constants();
-        // let bool_helper = |cond: bool, err| cond.then_some(()).ok_or(err);
-
-        let res = match (expected, self) {
-            // Easy correct cases
-            (Rational::Inf, FloatRes::Inf)
-            | (Rational::NegInf, FloatRes::NegInf)
-            | (Rational::Nan, FloatRes::Nan) => Ok(()),
-
-            // Easy incorrect cases
-            (
-                Rational::Inf,
-                FloatRes::NegInf | FloatRes::Zero | FloatRes::Nan | FloatRes::Real { .. },
-            ) => Err(CheckFailure::ExpectedInf),
-            (
-                Rational::NegInf,
-                FloatRes::Inf | FloatRes::Zero | FloatRes::Nan | FloatRes::Real { .. },
-            ) => Err(CheckFailure::ExpectedNegInf),
-            (
-                Rational::Nan,
-                FloatRes::Inf | FloatRes::NegInf | FloatRes::Zero | FloatRes::Real { .. },
-            ) => Err(CheckFailure::ExpectedNan),
-            (Rational::Finite(_), FloatRes::Nan) => Err(CheckFailure::UnexpectedNan),
-
-            // Cases near limits
-            (Rational::Finite(r), FloatRes::Zero) => {
-                if r <= consts.zero_cutoff {
-                    Ok(())
-                } else {
-                    Err(CheckFailure::UnexpectedZero)
-                }
-            }
-            (Rational::Finite(r), FloatRes::Inf) => {
-                if r >= consts.inf_cutoff {
-                    Ok(())
-                } else {
-                    Err(CheckFailure::UnexpectedInf)
-                }
-            }
-            (Rational::Finite(r), FloatRes::NegInf) => {
-                if r <= consts.neg_inf_cutoff {
-                    Ok(())
-                } else {
-                    Err(CheckFailure::UnexpectedNegInf)
-                }
-            }
-
-            // Actual numbers
-            (Rational::Finite(r), FloatRes::Real { sig, exp }) => Self::validate_real(r, sig, exp),
-        };
-
-        res.map_err(|fail| CheckError {
-            fail,
-            input: input.into(),
-            float_res: format!("{self:?}").into(),
-        })
-    }
-
-    /// Check that `sig * 2^exp` is the same as `rational`, within the float's error margin.
-    fn validate_real(rational: BigRational, sig: F::SInt, exp: i32) -> Result<(), CheckFailure> {
-        let consts = F::constants();
-
-        // `2^exp`. Use cached powers of two to be faster.
-        let two_exp = consts
-            .powers_of_two
-            .get(&exp)
-            .unwrap_or_else(|| panic!("missing exponent {exp} for {}", type_name::<F>()));
-
-        // Rational from the parsed value, `sig * 2^exp`
-        let parsed_rational = two_exp * sig.to_bigint().unwrap();
-        let error = (parsed_rational - &rational).abs();
-
-        // Determine acceptable error at this exponent, which is halfway between this value
-        // (`sig * 2^exp`) and the next value up (`(sig+1) * 2^exp`).
-        let half_ulp = consts.half_ulp.get(&exp).unwrap();
-
-        // If we are within one error value (but not equal) then we rounded correctly.
-        if &error < half_ulp {
-            return Ok(());
-        }
-
-        // For values where we are exactly between two representable values, meaning that the error
-        // is exactly one half of the precision at that exponent, we need to round to an even
-        // binary value (i.e. mantissa ends in 0).
-        let incorrect_midpoint_rounding = if &error == half_ulp {
-            if sig & F::SInt::ONE == F::SInt::ZERO {
-                return Ok(());
-            }
-
-            // We rounded to odd rather than even; failing based on midpoint rounding.
-            true
-        } else {
-            // We are out of spec for some other reason.
-            false
-        };
-
-        let one_ulp = consts.half_ulp.get(&(exp + 1)).unwrap();
-        assert_eq!(one_ulp, &(half_ulp * &consts.two), "ULP values are incorrect");
-
-        let relative_error = error / one_ulp;
-
-        Err(CheckFailure::InvalidReal {
-            error_float: relative_error.to_f64(),
-            error_str: relative_error.to_string().into(),
-            incorrect_midpoint_rounding,
-        })
-    }
-
-    /// Remove trailing zeros in the significand and adjust the exponent
-    #[cfg(test)]
-    fn normalize(self) -> Self {
-        use std::cmp::min;
-
-        match self {
-            Self::Real { sig, exp } => {
-                // If there are trailing zeroes, remove them and increment the exponent instead
-                let shift = min(sig.trailing_zeros(), exp.wrapping_neg().try_into().unwrap());
-                Self::Real { sig: sig >> shift, exp: exp + i32::try_from(shift).unwrap() }
-            }
-            _ => self,
-        }
-    }
-}
-
-/// Decompose a float into its integral components. This includes the implicit bit.
-///
-/// If `allow_nan` is `false`, panic if `NaN` values are reached.
-fn decode<F: Float>(f: F) -> FloatRes<F> {
-    let ione = F::SInt::ONE;
-    let izero = F::SInt::ZERO;
-
-    let mut exponent_biased = f.exponent();
-    let mut mantissa = f.mantissa().to_signed();
-
-    if exponent_biased == 0 {
-        if mantissa == izero {
-            return FloatRes::Zero;
-        }
-
-        exponent_biased += 1;
-    } else if exponent_biased == F::EXP_SAT {
-        if mantissa != izero {
-            return FloatRes::Nan;
-        }
-
-        if f.is_sign_negative() {
-            return FloatRes::NegInf;
-        }
-
-        return FloatRes::Inf;
-    } else {
-        // Set implicit bit
-        mantissa |= ione << F::MAN_BITS;
-    }
-
-    let mut exponent = i32::try_from(exponent_biased).unwrap();
-
-    // Adjust for bias and the rnage of the mantissa
-    exponent -= i32::try_from(F::EXP_BIAS + F::MAN_BITS).unwrap();
-
-    if f.is_sign_negative() {
-        mantissa = mantissa.wrapping_neg();
-    }
-
-    FloatRes::Real { sig: mantissa, exp: exponent }
-}
-
-/// A rational or its unrepresentable values.
-#[derive(Clone, Debug, PartialEq)]
-enum Rational {
-    Inf,
-    NegInf,
-    Nan,
-    Finite(BigRational),
-}
-
-impl Rational {
-    /// Turn a string into a rational. `None` if `NaN`.
-    fn parse(s: &str) -> Rational {
-        let mut s = s; // lifetime rules
-
-        if s.strip_prefix('+').unwrap_or(s).eq_ignore_ascii_case("nan")
-            || s.eq_ignore_ascii_case("-nan")
-        {
-            return Rational::Nan;
-        }
-
-        if s.strip_prefix('+').unwrap_or(s).eq_ignore_ascii_case("inf") {
-            return Rational::Inf;
-        }
-
-        if s.eq_ignore_ascii_case("-inf") {
-            return Rational::NegInf;
-        }
-
-        // Fast path; no decimals or exponents ot parse
-        if s.bytes().all(|b| b.is_ascii_digit() || b == b'-') {
-            return Rational::Finite(BigRational::from_str(s).unwrap());
-        }
-
-        let mut ten_exp: i32 = 0;
-
-        // Remove and handle e.g. `e-4`, `e+10`, `e5` suffixes
-        if let Some(pos) = s.bytes().position(|b| b == b'e' || b == b'E') {
-            let (dec, exp) = s.split_at(pos);
-            s = dec;
-            ten_exp = exp[1..].parse().unwrap();
-        }
-
-        // Remove the decimal and instead change our exponent
-        // E.g. "12.3456" becomes "123456 * 10^-4"
-        let mut s_owned;
-        if let Some(pos) = s.bytes().position(|b| b == b'.') {
-            ten_exp = ten_exp.checked_sub((s.len() - pos - 1).try_into().unwrap()).unwrap();
-            s_owned = s.to_owned();
-            s_owned.remove(pos);
-            s = &s_owned;
-        }
-
-        // let pow = BigRational::from_u32(10).unwrap().pow(ten_exp);
-        let pow =
-            POWERS_OF_TEN.get(&ten_exp).unwrap_or_else(|| panic!("missing power of ten {ten_exp}"));
-        let r = pow
-            * BigInt::from_str(s)
-                .unwrap_or_else(|e| panic!("`BigInt::from_str(\"{s}\")` failed with {e}"));
-        Rational::Finite(r)
-    }
-
-    #[cfg(test)]
-    fn expect_finite(self) -> BigRational {
-        let Self::Finite(r) = self else {
-            panic!("got non rational: {self:?}");
-        };
-
-        r
-    }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/etc/test-float-parse/src/validate/tests.rs b/src/etc/test-float-parse/src/validate/tests.rs
deleted file mode 100644
index ab0e7d8a7ba..00000000000
--- a/src/etc/test-float-parse/src/validate/tests.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-use num::ToPrimitive;
-
-use super::*;
-
-#[test]
-fn test_parse_rational() {
-    assert_eq!(Rational::parse("1234").expect_finite(), BigRational::new(1234.into(), 1.into()));
-    assert_eq!(
-        Rational::parse("-1234").expect_finite(),
-        BigRational::new((-1234).into(), 1.into())
-    );
-    assert_eq!(Rational::parse("1e+6").expect_finite(), BigRational::new(1000000.into(), 1.into()));
-    assert_eq!(Rational::parse("1e-6").expect_finite(), BigRational::new(1.into(), 1000000.into()));
-    assert_eq!(
-        Rational::parse("10.4e6").expect_finite(),
-        BigRational::new(10400000.into(), 1.into())
-    );
-    assert_eq!(
-        Rational::parse("10.4e+6").expect_finite(),
-        BigRational::new(10400000.into(), 1.into())
-    );
-    assert_eq!(
-        Rational::parse("10.4e-6").expect_finite(),
-        BigRational::new(13.into(), 1250000.into())
-    );
-    assert_eq!(
-        Rational::parse("10.4243566462342456234124").expect_finite(),
-        BigRational::new(104243566462342456234124_i128.into(), 10000000000000000000000_i128.into())
-    );
-    assert_eq!(Rational::parse("inf"), Rational::Inf);
-    assert_eq!(Rational::parse("+inf"), Rational::Inf);
-    assert_eq!(Rational::parse("-inf"), Rational::NegInf);
-    assert_eq!(Rational::parse("NaN"), Rational::Nan);
-}
-
-#[test]
-fn test_decode() {
-    assert_eq!(decode(0f32), FloatRes::Zero);
-    assert_eq!(decode(f32::INFINITY), FloatRes::Inf);
-    assert_eq!(decode(f32::NEG_INFINITY), FloatRes::NegInf);
-    assert_eq!(decode(1.0f32).normalize(), FloatRes::Real { sig: 1, exp: 0 });
-    assert_eq!(decode(-1.0f32).normalize(), FloatRes::Real { sig: -1, exp: 0 });
-    assert_eq!(decode(100.0f32).normalize(), FloatRes::Real { sig: 100, exp: 0 });
-    assert_eq!(decode(100.5f32).normalize(), FloatRes::Real { sig: 201, exp: -1 });
-    assert_eq!(decode(-4.004f32).normalize(), FloatRes::Real { sig: -8396997, exp: -21 });
-    assert_eq!(decode(0.0004f32).normalize(), FloatRes::Real { sig: 13743895, exp: -35 });
-    assert_eq!(decode(f32::from_bits(0x1)).normalize(), FloatRes::Real { sig: 1, exp: -149 });
-}
-
-#[test]
-fn test_validate() {
-    validate::<f32>("0").unwrap();
-    validate::<f32>("-0").unwrap();
-    validate::<f32>("1").unwrap();
-    validate::<f32>("-1").unwrap();
-    validate::<f32>("1.1").unwrap();
-    validate::<f32>("-1.1").unwrap();
-    validate::<f32>("1e10").unwrap();
-    validate::<f32>("1e1000").unwrap();
-    validate::<f32>("-1e1000").unwrap();
-    validate::<f32>("1e-1000").unwrap();
-    validate::<f32>("-1e-1000").unwrap();
-}
-
-#[test]
-fn test_validate_real() {
-    // Most of the arbitrary values come from checking against <http://weitz.de/ieee/>.
-    let r = &BigRational::from_float(10.0).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 10, 0).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 10, -1).unwrap_err();
-    FloatRes::<f32>::validate_real(r.clone(), 10, 1).unwrap_err();
-
-    let r = &BigRational::from_float(0.25).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 1, -2).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 2, -2).unwrap_err();
-
-    let r = &BigRational::from_float(1234.5678).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 0b100110100101001000101011, -13).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), 0b100110100101001000101010, -13).unwrap_err();
-    FloatRes::<f32>::validate_real(r.clone(), 0b100110100101001000101100, -13).unwrap_err();
-
-    let r = &BigRational::from_float(-1234.5678).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), -0b100110100101001000101011, -13).unwrap();
-    FloatRes::<f32>::validate_real(r.clone(), -0b100110100101001000101010, -13).unwrap_err();
-    FloatRes::<f32>::validate_real(r.clone(), -0b100110100101001000101100, -13).unwrap_err();
-}
-
-#[test]
-#[allow(unused)]
-fn test_validate_real_rounding() {
-    // Check that we catch when values don't round to even.
-
-    // For f32, the cutoff between 1.0 and the next value up (1.0000001) is
-    // 1.000000059604644775390625. Anything below it should round down, anything above it should
-    // round up, and the value itself should round _down_ because `1.0` has an even significand but
-    // 1.0000001 is odd.
-    let v1_low_down = Rational::parse("1.00000005960464477539062499999").expect_finite();
-    let v1_mid_down = Rational::parse("1.000000059604644775390625").expect_finite();
-    let v1_high_up = Rational::parse("1.00000005960464477539062500001").expect_finite();
-
-    let exp = -(f32::MAN_BITS as i32);
-    let v1_down_sig = 1 << f32::MAN_BITS;
-    let v1_up_sig = (1 << f32::MAN_BITS) | 0b1;
-
-    FloatRes::<f32>::validate_real(v1_low_down.clone(), v1_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(v1_mid_down.clone(), v1_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(v1_high_up.clone(), v1_up_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v1_low_down.clone(), -v1_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v1_mid_down.clone(), -v1_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v1_high_up.clone(), -v1_up_sig, exp).unwrap();
-
-    // 1.000000178813934326171875 is between 1.0000001 and the next value up, 1.0000002. The middle
-    // value here should round _up_ since 1.0000002 has an even mantissa.
-    let v2_low_down = Rational::parse("1.00000017881393432617187499999").expect_finite();
-    let v2_mid_up = Rational::parse("1.000000178813934326171875").expect_finite();
-    let v2_high_up = Rational::parse("1.00000017881393432617187500001").expect_finite();
-
-    let v2_down_sig = v1_up_sig;
-    let v2_up_sig = (1 << f32::MAN_BITS) | 0b10;
-
-    FloatRes::<f32>::validate_real(v2_low_down.clone(), v2_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(v2_mid_up.clone(), v2_up_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(v2_high_up.clone(), v2_up_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v2_low_down.clone(), -v2_down_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v2_mid_up.clone(), -v2_up_sig, exp).unwrap();
-    FloatRes::<f32>::validate_real(-v2_high_up.clone(), -v2_up_sig, exp).unwrap();
-
-    // Rounding the wrong direction should error
-    for res in [
-        FloatRes::<f32>::validate_real(v1_mid_down.clone(), v1_up_sig, exp),
-        FloatRes::<f32>::validate_real(v2_mid_up.clone(), v2_down_sig, exp),
-        FloatRes::<f32>::validate_real(-v1_mid_down.clone(), -v1_up_sig, exp),
-        FloatRes::<f32>::validate_real(-v2_mid_up.clone(), -v2_down_sig, exp),
-    ] {
-        let e = res.unwrap_err();
-        let CheckFailure::InvalidReal { incorrect_midpoint_rounding: true, .. } = e else {
-            panic!("{e:?}");
-        };
-    }
-}
-
-/// Just a quick check that the constants are what we expect.
-#[test]
-fn check_constants() {
-    assert_eq!(f32::constants().max.to_f32().unwrap(), f32::MAX);
-    assert_eq!(f32::constants().min_subnormal.to_f32().unwrap(), f32::from_bits(0x1));
-    assert_eq!(f64::constants().max.to_f64().unwrap(), f64::MAX);
-    assert_eq!(f64::constants().min_subnormal.to_f64().unwrap(), f64::from_bits(0x1));
-}