core: fixed a slight bug.
The bug involves the incorrect logic for `core::num::flt2dec::decoder`. This makes some numbers in the form of 2^n missing one final digits, which breaks the bijectivity criterion. The regression tests have been added, and f32 exhaustive test is rerun to get the updated result.
This commit is contained in:
parent
8a195f0754
commit
97ea7c14ba
@ -75,7 +75,7 @@ pub fn decode<T: DecodableFloat>(v: T) -> (/*negative?*/ bool, FullDecoded) {
|
||||
FpCategory::Infinite => FullDecoded::Infinite,
|
||||
FpCategory::Zero => FullDecoded::Zero,
|
||||
FpCategory::Subnormal => {
|
||||
// (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp)
|
||||
// neighbors: (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp)
|
||||
// Float::integer_decode always preserves the exponent,
|
||||
// so the mantissa is scaled for subnormals.
|
||||
FullDecoded::Finite(Decoded { mant: mant, minus: 1, plus: 1,
|
||||
@ -83,13 +83,13 @@ pub fn decode<T: DecodableFloat>(v: T) -> (/*negative?*/ bool, FullDecoded) {
|
||||
}
|
||||
FpCategory::Normal => {
|
||||
let minnorm = <T as DecodableFloat>::min_pos_norm_value().integer_decode();
|
||||
if mant == minnorm.0 && exp == minnorm.1 {
|
||||
// (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp)
|
||||
if mant == minnorm.0 {
|
||||
// neighbors: (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp)
|
||||
// where maxmant = minnormmant * 2 - 1
|
||||
FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 2,
|
||||
exp: exp - 1, inclusive: even })
|
||||
FullDecoded::Finite(Decoded { mant: mant << 2, minus: 1, plus: 2,
|
||||
exp: exp - 2, inclusive: even })
|
||||
} else {
|
||||
// (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp)
|
||||
// neighbors: (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp)
|
||||
FullDecoded::Finite(Decoded { mant: mant << 1, minus: 1, plus: 1,
|
||||
exp: exp - 1, inclusive: even })
|
||||
}
|
||||
|
@ -216,6 +216,13 @@ pub fn f32_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
|
||||
// 10^18 * 0.314159231156617216
|
||||
check_shortest!(f(3.141592e17f32) => b"3141592", 18);
|
||||
|
||||
// regression test for decoders
|
||||
// 10^8 * 0.3355443
|
||||
// 10^8 * 0.33554432
|
||||
// 10^8 * 0.33554436
|
||||
let twoto25: f32 = StdFloat::ldexp(1.0, 25);
|
||||
check_shortest!(f(twoto25) => b"33554432", 8);
|
||||
|
||||
// 10^39 * 0.340282326356119256160033759537265639424
|
||||
// 10^39 * 0.34028234663852885981170418348451692544
|
||||
// 10^39 * 0.340282366920938463463374607431768211456
|
||||
@ -308,6 +315,13 @@ pub fn f64_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
|
||||
// 10^18 * 0.314159200000000064
|
||||
check_shortest!(f(3.141592e17f64) => b"3141592", 18);
|
||||
|
||||
// regression test for decoders
|
||||
// 10^20 * 0.18446744073709549568
|
||||
// 10^20 * 0.18446744073709551616
|
||||
// 10^20 * 0.18446744073709555712
|
||||
let twoto64: f64 = StdFloat::ldexp(1.0, 64);
|
||||
check_shortest!(f(twoto64) => b"18446744073709552", 20);
|
||||
|
||||
// pathological case: high = 10^23 (exact). tie breaking should always prefer that.
|
||||
// 10^24 * 0.099999999999999974834176
|
||||
// 10^24 * 0.099999999999999991611392
|
||||
@ -492,7 +506,7 @@ pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
|
||||
// so why not simply testing all of them?
|
||||
//
|
||||
// this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
|
||||
// but with `-O3 -C lto` this only takes about two hours or so.
|
||||
// but with `-C opt-level=3 -C lto` this only takes about an hour or so.
|
||||
|
||||
// iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges
|
||||
let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test",
|
||||
@ -500,7 +514,7 @@ pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
|
||||
let x: f32 = unsafe {mem::transmute(i as u32 + 1)};
|
||||
decode_finite(x)
|
||||
});
|
||||
assert_eq!((npassed, nignored), (2121451879, 17643160));
|
||||
assert_eq!((npassed, nignored), (2121451881, 17643158));
|
||||
}
|
||||
|
||||
fn to_string_with_parts<F>(mut f: F) -> String
|
||||
|
@ -60,7 +60,7 @@ fn shortest_f32_exhaustive_equivalence_test() {
|
||||
//
|
||||
// this reports the progress and the number of f32 values returned `None`.
|
||||
// with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
|
||||
// `done, ignored=17643160 passed=2121451879 failed=0`.
|
||||
// `done, ignored=17643158 passed=2121451881 failed=0`.
|
||||
|
||||
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
|
||||
f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
|
||||
|
Loading…
Reference in New Issue
Block a user