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:
Kang Seonghoon 2015-04-21 20:32:25 +09:00
parent 8a195f0754
commit 97ea7c14ba
3 changed files with 23 additions and 9 deletions

View File

@ -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 })
}

View File

@ -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

View File

@ -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);