Skip to content

Commit c0a6814

Browse files
committed
Rewrite test-float-parse in Rust
The existing implementation uses Python to launch a set of Rust-written binaries. Unfortunately, this is currently broken; it seems that some updates meant it no longer compiles. There is also a problem that support for more float types (`f16`, `f128`) would be difficult to add since this is very specialized to `f32` and `f64`. Because of these sortcomings, migrate to a version written in Rust. This version should be significantly faster; test generators can execute in parallel, and test cases are chunked and parallelized. This should also resolve the preexisting "... the worker processes are leaked and stick around forever" comment. This change also introduces genericism over float types and properties, meaning it will be much easier to extend support to newly added types. `num::BigRational` is used in place of Python's fractions for infinite-precision calculations.
1 parent 99b7134 commit c0a6814

28 files changed

+2337
-554
lines changed

src/etc/test-float-parse/Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ version = "0.1.0"
44
edition = "2021"
55
publish = false
66

7-
[workspace]
8-
resolver = "1"
9-
107
[dependencies]
11-
rand = "0.8"
8+
indicatif = { version = "0.17.8", default-features = false }
9+
num = "0.4.3"
10+
rand = "0.8.5"
11+
rand_chacha = "0.3"
12+
rayon = "1"
1213

1314
[lib]
1415
name = "test_float_parse"

src/etc/test-float-parse/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Float Parsing Tests
2+
3+
These are tests designed to test decimal to float conversions (`dec2flt`) used
4+
by the standard library.
5+
6+
It consistes of a collection of test generators that each generate a set of
7+
patterns intended to test a specific property. In addition, there are exhaustive
8+
tests (for <= `f32`) and fuzzers (for anything that can't be run exhaustively).
9+
10+
The generators work as follows:
11+
12+
- Each generator is a struct that lives somewhere in the `gen` module. Usually
13+
it is generic over a float type.
14+
- These generators must implement `Iterator`, which should return a context type
15+
that can be used to construct a test string (but usually not the string
16+
itself).
17+
- They must also implement the `Generator` trait, which provides a method to
18+
write test context to a string as a test case, as well as some extra metadata.
19+
20+
The split between context generation and string construction is so that we can
21+
reuse string allocations.
22+
- Each generator gets registered once for each float type. Each of these
23+
generators then get their iterator called, and each test case checked against
24+
the float type's parse implementation.
25+
26+
Some generators produce decimal strings, others create bit patterns that need to
27+
be bitcasted to the float type, which then uses its `Display` implementation to
28+
write to a string. For these, float to decimal (`flt2dec`) conversions also get
29+
tested, if unintentionally.
30+
31+
For each test case, the following is done:
32+
33+
- The test string is parsed to the float type using the standard library's
34+
implementation.
35+
- The test string is parsed separately to a `BigRational`, which acts as a
36+
representation with infinite precision.
37+
- The rational value then gets checked that it is within the float's
38+
representable values (absolute value greater than the smallest number to round
39+
to zero, but less less than the first value to round to infinity). If these
40+
limits are exceeded, check that the parsed float reflects that.
41+
- For real nonzero numbers, the parsed float is converted into a rational using
42+
`significand * 2^exponent`. It is then checked against the actual rational
43+
value, and verified to be within half a bit's precision of the parsed value.
44+
Also it is checked that ties round to even.
45+
46+
This is all highly parallelized with `rayon`; test generators can run in
47+
parallel, and their tests get chunked and run in parallel.
48+
49+
There is a simple command line that allows filtering which tests are run,
50+
setting the number of iterations for fuzzing tests, limiting failures, setting
51+
timeouts, etc. See `main.rs` or run with `--help` for options.
52+
53+
Note that when running via `./x`, only tests that take less than a few minutes
54+
are run by default. Navigate to the crate (or pass `-C` to Cargo) and run it
55+
directly to run all tests or pass specific arguments.

0 commit comments

Comments
 (0)