Skip to content

Commit

Permalink
core: fixed a slight bug.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lifthrasiir committed May 6, 2015
1 parent 8a195f0 commit 97ea7c1
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 9 deletions.
12 changes: 6 additions & 6 deletions src/libcore/num/flt2dec/decoder.rs
Expand Up @@ -75,21 +75,21 @@ 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,
exp: exp, inclusive: even })
}
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 })
}
Expand Down
18 changes: 16 additions & 2 deletions src/libcoretest/num/flt2dec/mod.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -492,15 +506,15 @@ 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",
k, 0x7f7f_ffff, f, g, |i: 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
Expand Down
2 changes: 1 addition & 1 deletion src/libcoretest/num/flt2dec/strategy/grisu.rs
Expand Up @@ -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);
Expand Down

0 comments on commit 97ea7c1

Please sign in to comment.