about summary refs log tree commit diff
path: root/src/libcoretest
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-05-09 14:56:56 +0000
committerbors <bors@rust-lang.org>2015-05-09 14:56:56 +0000
commit67ba6dcf68860d8a20891faad88a33b35ed58df5 (patch)
tree1793c023e25949a282b8a95ba1ef090070c97a19 /src/libcoretest
parent497942332f919b1a952310a730349ca1b9524968 (diff)
parent1aecd17463bb16d03462cf31edb920b9f47ddf2c (diff)
downloadrust-67ba6dcf68860d8a20891faad88a33b35ed58df5.tar.gz
rust-67ba6dcf68860d8a20891faad88a33b35ed58df5.zip
Auto merge of #24612 - lifthrasiir:flt2dec, r=pnkfelix
This is a direct port of my prior work on the float formatting. The detailed description is available [here](https://github.com/lifthrasiir/rust-strconv#flt2dec). In brief,

* This adds a new hidden module `core::num::flt2dec` for testing from `libcoretest`. Why is it in `core::num` instead of `core::fmt`? Because I envision that the table used by `flt2dec` is directly applicable to `dec2flt` (cf. #24557) as well, which exceeds the realm of "formatting".
* This contains both Dragon4 algorithm (exact, complete but slow) and Grisu3 algorithm (exact, fast but incomplete).
* The code is accompanied with a large amount of self-tests and some exhaustive tests. In particular, `libcoretest` gets a new dependency on `librand`. For the external interface it relies on the existing test suite.
* It is known that, in the best case, the entire formatting code has about 30 KBs of binary overhead (judged from strconv experiments). Not too bad but there might be a potential room for improvements.

This is rather large code. I did my best to comment and annotate the code, but you have been warned.

For the maximal availability the original code was licensed in CC0, but I've also dual-licensed it in MIT/Apache as well so there should be no licensing concern.

This is [breaking-change] as it changes the float output slightly (and it also affects the casing of `inf` and `nan`). I hope this is not a big deal though :)

Fixes #7030, #18038 and #24556. Also related to #6220 and #20870.

## Known Issues

- [x] I've yet to finish `make check-stage1`. It does pass main test suites including `run-pass` but there might be some unknown edges on the doctests.
- [ ] Figure out how this PR affects rustc.
- [ ] Determine which internal routine is mapped to the formatting specifier. Depending on the decision, some internal routine can be safely removed (for instance, currently `to_shortest_str` is unused).
Diffstat (limited to 'src/libcoretest')
-rw-r--r--src/libcoretest/lib.rs1
-rw-r--r--src/libcoretest/num/flt2dec/bignum.rs160
-rw-r--r--src/libcoretest/num/flt2dec/estimator.rs61
-rw-r--r--src/libcoretest/num/flt2dec/mod.rs1178
-rw-r--r--src/libcoretest/num/flt2dec/strategy/dragon.rs117
-rw-r--r--src/libcoretest/num/flt2dec/strategy/grisu.rs177
-rw-r--r--src/libcoretest/num/mod.rs2
7 files changed, 1696 insertions, 0 deletions
diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs
index 15669722752..90c1e8b132e 100644
--- a/src/libcoretest/lib.rs
+++ b/src/libcoretest/lib.rs
@@ -30,6 +30,7 @@ extern crate core;
 extern crate test;
 extern crate libc;
 extern crate rustc_unicode;
+extern crate rand;
 
 mod any;
 mod atomic;
diff --git a/src/libcoretest/num/flt2dec/bignum.rs b/src/libcoretest/num/flt2dec/bignum.rs
new file mode 100644
index 00000000000..09a1ed41dad
--- /dev/null
+++ b/src/libcoretest/num/flt2dec/bignum.rs
@@ -0,0 +1,160 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::prelude::v1::*;
+use core::num::flt2dec::bignum::tests::Big8x3 as Big;
+
+#[test]
+#[should_panic]
+fn test_from_u64_overflow() {
+    Big::from_u64(0x1000000);
+}
+
+#[test]
+fn test_add() {
+    assert_eq!(*Big::from_small(3).add(&Big::from_small(4)), Big::from_small(7));
+    assert_eq!(*Big::from_small(3).add(&Big::from_small(0)), Big::from_small(3));
+    assert_eq!(*Big::from_small(0).add(&Big::from_small(3)), Big::from_small(3));
+    assert_eq!(*Big::from_small(3).add(&Big::from_u64(0xfffe)), Big::from_u64(0x10001));
+    assert_eq!(*Big::from_u64(0xfedc).add(&Big::from_u64(0x789)), Big::from_u64(0x10665));
+    assert_eq!(*Big::from_u64(0x789).add(&Big::from_u64(0xfedc)), Big::from_u64(0x10665));
+}
+
+#[test]
+#[should_panic]
+fn test_add_overflow_1() {
+    Big::from_small(1).add(&Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_add_overflow_2() {
+    Big::from_u64(0xffffff).add(&Big::from_small(1));
+}
+
+#[test]
+fn test_sub() {
+    assert_eq!(*Big::from_small(7).sub(&Big::from_small(4)), Big::from_small(3));
+    assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x789)), Big::from_u64(0xfedc));
+    assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0xfedc)), Big::from_u64(0x789));
+    assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10664)), Big::from_small(1));
+    assert_eq!(*Big::from_u64(0x10665).sub(&Big::from_u64(0x10665)), Big::from_small(0));
+}
+
+#[test]
+#[should_panic]
+fn test_sub_underflow_1() {
+    Big::from_u64(0x10665).sub(&Big::from_u64(0x10666));
+}
+
+#[test]
+#[should_panic]
+fn test_sub_underflow_2() {
+    Big::from_small(0).sub(&Big::from_u64(0x123456));
+}
+
+#[test]
+fn test_mul_small() {
+    assert_eq!(*Big::from_small(7).mul_small(5), Big::from_small(35));
+    assert_eq!(*Big::from_small(0xff).mul_small(0xff), Big::from_u64(0xfe01));
+    assert_eq!(*Big::from_u64(0xffffff/13).mul_small(13), Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_small_overflow() {
+    Big::from_u64(0x800000).mul_small(2);
+}
+
+#[test]
+fn test_mul_pow2() {
+    assert_eq!(*Big::from_small(0x7).mul_pow2(4), Big::from_small(0x70));
+    assert_eq!(*Big::from_small(0xff).mul_pow2(1), Big::from_u64(0x1fe));
+    assert_eq!(*Big::from_small(0xff).mul_pow2(12), Big::from_u64(0xff000));
+    assert_eq!(*Big::from_small(0x1).mul_pow2(23), Big::from_u64(0x800000));
+    assert_eq!(*Big::from_u64(0x123).mul_pow2(0), Big::from_u64(0x123));
+    assert_eq!(*Big::from_u64(0x123).mul_pow2(7), Big::from_u64(0x9180));
+    assert_eq!(*Big::from_u64(0x123).mul_pow2(15), Big::from_u64(0x918000));
+    assert_eq!(*Big::from_small(0).mul_pow2(23), Big::from_small(0));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow2_overflow_1() {
+    Big::from_u64(0x1).mul_pow2(24);
+}
+
+#[test]
+#[should_panic]
+fn test_mul_pow2_overflow_2() {
+    Big::from_u64(0x123).mul_pow2(16);
+}
+
+#[test]
+fn test_mul_digits() {
+    assert_eq!(*Big::from_small(3).mul_digits(&[5]), Big::from_small(15));
+    assert_eq!(*Big::from_small(0xff).mul_digits(&[0xff]), Big::from_u64(0xfe01));
+    assert_eq!(*Big::from_u64(0x123).mul_digits(&[0x56, 0x4]), Big::from_u64(0x4edc2));
+    assert_eq!(*Big::from_u64(0x12345).mul_digits(&[0x67]), Big::from_u64(0x7530c3));
+    assert_eq!(*Big::from_small(0x12).mul_digits(&[0x67, 0x45, 0x3]), Big::from_u64(0x3ae13e));
+    assert_eq!(*Big::from_u64(0xffffff/13).mul_digits(&[13]), Big::from_u64(0xffffff));
+    assert_eq!(*Big::from_small(13).mul_digits(&[0x3b, 0xb1, 0x13]), Big::from_u64(0xffffff));
+}
+
+#[test]
+#[should_panic]
+fn test_mul_digits_overflow_1() {
+    Big::from_u64(0x800000).mul_digits(&[2]);
+}
+
+#[test]
+#[should_panic]
+fn test_mul_digits_overflow_2() {
+    Big::from_u64(0x1000).mul_digits(&[0, 0x10]);
+}
+
+#[test]
+fn test_div_rem_small() {
+    let as_val = |(q, r): (&mut Big, u8)| (q.clone(), r);
+    assert_eq!(as_val(Big::from_small(0xff).div_rem_small(15)), (Big::from_small(17), 0));
+    assert_eq!(as_val(Big::from_small(0xff).div_rem_small(16)), (Big::from_small(15), 15));
+    assert_eq!(as_val(Big::from_small(3).div_rem_small(40)), (Big::from_small(0), 3));
+    assert_eq!(as_val(Big::from_u64(0xffffff).div_rem_small(123)),
+               (Big::from_u64(0xffffff / 123), (0xffffffu64 % 123) as u8));
+    assert_eq!(as_val(Big::from_u64(0x10000).div_rem_small(123)),
+               (Big::from_u64(0x10000 / 123), (0x10000u64 % 123) as u8));
+}
+
+#[test]
+fn test_is_zero() {
+    assert!(Big::from_small(0).is_zero());
+    assert!(!Big::from_small(3).is_zero());
+    assert!(!Big::from_u64(0x123).is_zero());
+    assert!(!Big::from_u64(0xffffff).sub(&Big::from_u64(0xfffffe)).is_zero());
+    assert!(Big::from_u64(0xffffff).sub(&Big::from_u64(0xffffff)).is_zero());
+}
+
+#[test]
+fn test_ord() {
+    assert!(Big::from_u64(0) < Big::from_u64(0xffffff));
+    assert!(Big::from_u64(0x102) < Big::from_u64(0x201));
+}
+
+#[test]
+fn test_fmt() {
+    assert_eq!(format!("{:?}", Big::from_u64(0)), "0x0");
+    assert_eq!(format!("{:?}", Big::from_u64(0x1)), "0x1");
+    assert_eq!(format!("{:?}", Big::from_u64(0x12)), "0x12");
+    assert_eq!(format!("{:?}", Big::from_u64(0x123)), "0x1_23");
+    assert_eq!(format!("{:?}", Big::from_u64(0x1234)), "0x12_34");
+    assert_eq!(format!("{:?}", Big::from_u64(0x12345)), "0x1_23_45");
+    assert_eq!(format!("{:?}", Big::from_u64(0x123456)), "0x12_34_56");
+}
+
diff --git a/src/libcoretest/num/flt2dec/estimator.rs b/src/libcoretest/num/flt2dec/estimator.rs
new file mode 100644
index 00000000000..21260c520f6
--- /dev/null
+++ b/src/libcoretest/num/flt2dec/estimator.rs
@@ -0,0 +1,61 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::f64;
+use core::num::flt2dec::estimator::*;
+
+#[test]
+fn test_estimate_scaling_factor() {
+    macro_rules! assert_almost_eq {
+        ($actual:expr, $expected:expr) => ({
+            let actual = $actual;
+            let expected = $expected;
+            println!("{} - {} = {} - {} = {}", stringify!($expected), stringify!($actual),
+                     expected, actual, expected - actual);
+            assert!(expected == actual || expected == actual + 1,
+                    "expected {}, actual {}", expected, actual);
+        })
+    }
+
+    assert_almost_eq!(estimate_scaling_factor(1, 0), 0);
+    assert_almost_eq!(estimate_scaling_factor(2, 0), 1);
+    assert_almost_eq!(estimate_scaling_factor(10, 0), 1);
+    assert_almost_eq!(estimate_scaling_factor(11, 0), 2);
+    assert_almost_eq!(estimate_scaling_factor(100, 0), 2);
+    assert_almost_eq!(estimate_scaling_factor(101, 0), 3);
+    assert_almost_eq!(estimate_scaling_factor(10000000000000000000, 0), 19);
+    assert_almost_eq!(estimate_scaling_factor(10000000000000000001, 0), 20);
+
+    // 1/2^20 = 0.00000095367...
+    assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000, -20), -6);
+    assert_almost_eq!(estimate_scaling_factor(1 * 1048576 / 1000000 + 1, -20), -5);
+    assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000, -20), -5);
+    assert_almost_eq!(estimate_scaling_factor(10 * 1048576 / 1000000 + 1, -20), -4);
+    assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000, -20), -4);
+    assert_almost_eq!(estimate_scaling_factor(100 * 1048576 / 1000000 + 1, -20), -3);
+    assert_almost_eq!(estimate_scaling_factor(1048575, -20), 0);
+    assert_almost_eq!(estimate_scaling_factor(1048576, -20), 0);
+    assert_almost_eq!(estimate_scaling_factor(1048577, -20), 1);
+    assert_almost_eq!(estimate_scaling_factor(10485759999999999999, -20), 13);
+    assert_almost_eq!(estimate_scaling_factor(10485760000000000000, -20), 13);
+    assert_almost_eq!(estimate_scaling_factor(10485760000000000001, -20), 14);
+
+    // extreme values:
+    // 2^-1074 = 4.94065... * 10^-324
+    // (2^53-1) * 2^971 = 1.79763... * 10^308
+    assert_almost_eq!(estimate_scaling_factor(1, -1074), -323);
+    assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309);
+
+    for i in -1074..972 {
+        let expected = f64::ldexp(1.0, i).log10().ceil();
+        assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16);
+    }
+}
+
diff --git a/src/libcoretest/num/flt2dec/mod.rs b/src/libcoretest/num/flt2dec/mod.rs
new file mode 100644
index 00000000000..488cd3a779c
--- /dev/null
+++ b/src/libcoretest/num/flt2dec/mod.rs
@@ -0,0 +1,1178 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::prelude::v1::*;
+use std::{str, mem, i16, f32, f64, fmt};
+use std::slice::bytes;
+use std::__rand as rand;
+use rand::{Rand, XorShiftRng};
+use rand::distributions::{IndependentSample, Range};
+
+use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};
+use core::num::flt2dec::{MAX_SIG_DIGITS, round_up, Part, Formatted, Sign};
+use core::num::flt2dec::{to_shortest_str, to_shortest_exp_str,
+                         to_exact_exp_str, to_exact_fixed_str};
+
+pub use test::Bencher;
+
+mod estimator;
+mod bignum;
+mod strategy {
+    mod dragon;
+    mod grisu;
+}
+
+pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
+    match decode(v).1 {
+        FullDecoded::Finite(decoded) => decoded,
+        full_decoded => panic!("expected finite, got {:?} instead", full_decoded)
+    }
+}
+
+macro_rules! check_shortest {
+    ($f:ident($v:expr) => $buf:expr, $exp:expr) => (
+        check_shortest!($f($v) => $buf, $exp;
+                        "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}",
+                        v = stringify!($v))
+    );
+
+    ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => (
+        check_shortest!($f{$($k: $v),+} => $buf, $exp;
+                        "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}",
+                        v = Decoded { $($k: $v),+ })
+    );
+
+    ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({
+        let mut buf = [b'_'; MAX_SIG_DIGITS];
+        let (len, k) = $f(&decode_finite($v), &mut buf);
+        assert!((&buf[..len], k) == ($buf, $exp),
+                $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k),
+                      expected = (str::from_utf8($buf).unwrap(), $exp),
+                      $($key = $val),*);
+    });
+
+    ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr;
+                                         $fmt:expr, $($key:ident = $val:expr),*) => ({
+        let mut buf = [b'_'; MAX_SIG_DIGITS];
+        let (len, k) = $f(&Decoded { $($k: $v),+ }, &mut buf);
+        assert!((&buf[..len], k) == ($buf, $exp),
+                $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k),
+                      expected = (str::from_utf8($buf).unwrap(), $exp),
+                      $($key = $val),*);
+    })
+}
+
+macro_rules! try_exact {
+    ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr;
+                                $fmt:expr, $($key:ident = $val:expr),*) => ({
+        let (len, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN);
+        assert!((&$buf[..len], k) == ($expected, $expectedk),
+                $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k),
+                      expected = (str::from_utf8($expected).unwrap(), $expectedk),
+                      $($key = $val),*);
+    })
+}
+
+macro_rules! try_fixed {
+    ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr;
+                                $fmt:expr, $($key:ident = $val:expr),*) => ({
+        let (len, k) = $f($decoded, &mut $buf[..], $request);
+        assert!((&$buf[..len], k) == ($expected, $expectedk),
+                $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k),
+                      expected = (str::from_utf8($expected).unwrap(), $expectedk),
+                      $($key = $val),*);
+    })
+}
+
+fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16)
+        where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    // use a large enough buffer
+    let mut buf = [b'_'; 1024];
+    let mut expected_ = [b'_'; 1024];
+
+    let decoded = decode_finite(v);
+    let cut = expected.iter().position(|&c| c == b' ');
+
+    // check significant digits
+    for i in 1..cut.unwrap_or(expected.len() - 1) {
+        bytes::copy_memory(&expected[..i], &mut expected_);
+        let mut expectedk_ = expectedk;
+        if expected[i] >= b'5' {
+            // check if this is a rounding-to-even case.
+            // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even.
+            if !(i+1 < expected.len() && expected[i-1] & 1 == 0 &&
+                                         expected[i] == b'5' &&
+                                         expected[i+1] == b' ') {
+                // if this returns true, expected_[..i] is all `9`s and being rounded up.
+                // we should always return `100..00` (`i` digits) instead, since that's
+                // what we can came up with `i` digits anyway. `round_up` assumes that
+                // the adjustment to the length is done by caller, which we simply ignore.
+                if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; }
+            }
+        }
+
+        try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_;
+                   "exact sigdigit mismatch for v={v}, i={i}: \
+                    actual {actual:?}, expected {expected:?}",
+                   v = vstr, i = i);
+        try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_;
+                   "fixed sigdigit mismatch for v={v}, i={i}: \
+                    actual {actual:?}, expected {expected:?}",
+                   v = vstr, i = i);
+    }
+
+    // check exact rounding for zero- and negative-width cases
+    let start;
+    if expected[0] >= b'5' {
+        try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1;
+                   "zero-width rounding-up mismatch for v={v}: \
+                    actual {actual:?}, expected {expected:?}",
+                   v = vstr);
+        start = 1;
+    } else {
+        start = 0;
+    }
+    for i in start..-10 {
+        try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk;
+                   "rounding-down mismatch for v={v}, i={i}: \
+                    actual {actual:?}, expected {expected:?}",
+                   v = vstr, i = -i);
+    }
+
+    // check infinite zero digits
+    if let Some(cut) = cut {
+        for i in cut..expected.len()-1 {
+            bytes::copy_memory(&expected[..cut], &mut expected_);
+            for c in &mut expected_[cut..i] { *c = b'0'; }
+
+            try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk;
+                       "exact infzero mismatch for v={v}, i={i}: \
+                        actual {actual:?}, expected {expected:?}",
+                       v = vstr, i = i);
+            try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk;
+                       "fixed infzero mismatch for v={v}, i={i}: \
+                        actual {actual:?}, expected {expected:?}",
+                       v = vstr, i = i);
+        }
+    }
+}
+
+fn check_exact_one<F, T>(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16)
+        where T: DecodableFloat + fmt::Display,
+              F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    // use a large enough buffer
+    let mut buf = [b'_'; 1024];
+    let v: T = DecodableFloat::ldexpi(x, e);
+    let decoded = decode_finite(v);
+
+    try_exact!(f(&decoded) => &mut buf, &expected, expectedk;
+               "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}",
+               x = x, e = e, t = tstr);
+    try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk;
+               "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}",
+               x = x, e = e, t = tstr);
+}
+
+macro_rules! check_exact {
+    ($f:ident($v:expr) => $buf:expr, $exp:expr) => (
+        check_exact(|d,b,k| $f(d,b,k), $v, stringify!($v), $buf, $exp)
+    )
+}
+
+macro_rules! check_exact_one {
+    ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => (
+        check_exact_one::<_, $t>(|d,b,k| $f(d,b,k), $x, $e, stringify!($t), $buf, $exp)
+    )
+}
+
+// in the following comments, three numbers are spaced by 1 ulp apart,
+// and the second one is being formatted.
+//
+// some tests are derived from [1].
+//
+// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion
+//     ftp://ftp.ee.lbl.gov/testbase-report.ps.Z
+
+pub fn f32_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    // 0.0999999940395355224609375
+    // 0.100000001490116119384765625
+    // 0.10000000894069671630859375
+    check_shortest!(f(0.1f32) => b"1", 0);
+
+    // 0.333333313465118408203125
+    // 0.3333333432674407958984375 (1/3 in the default rounding)
+    // 0.33333337306976318359375
+    check_shortest!(f(1.0f32/3.0) => b"33333334", 0);
+
+    // 10^1 * 0.31415917873382568359375
+    // 10^1 * 0.31415920257568359375
+    // 10^1 * 0.31415922641754150390625
+    check_shortest!(f(3.141592f32) => b"3141592", 1);
+
+    // 10^18 * 0.31415916243714048
+    // 10^18 * 0.314159196796878848
+    // 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
+    check_shortest!(f(f32::ldexp(1.0, 25)) => b"33554432", 8);
+
+    // 10^39 * 0.340282326356119256160033759537265639424
+    // 10^39 * 0.34028234663852885981170418348451692544
+    // 10^39 * 0.340282366920938463463374607431768211456
+    check_shortest!(f(f32::MAX) => b"34028235", 39);
+
+    // 10^-37 * 0.1175494210692441075487029444849287348827...
+    // 10^-37 * 0.1175494350822287507968736537222245677818...
+    // 10^-37 * 0.1175494490952133940450443629595204006810...
+    check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37);
+
+    // 10^-44 * 0
+    // 10^-44 * 0.1401298464324817070923729583289916131280...
+    // 10^-44 * 0.2802596928649634141847459166579832262560...
+    let minf32 = f32::ldexp(1.0, -149);
+    check_shortest!(f(minf32) => b"1", -44);
+}
+
+pub fn f32_exact_sanity_test<F>(mut f: F)
+        where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    let minf32 = f32::ldexp(1.0, -149);
+
+    check_exact!(f(0.1f32)            => b"100000001490116119384765625             ", 0);
+    check_exact!(f(0.5f32)            => b"5                                       ", 0);
+    check_exact!(f(1.0f32/3.0)        => b"3333333432674407958984375               ", 0);
+    check_exact!(f(3.141592f32)       => b"31415920257568359375                    ", 1);
+    check_exact!(f(3.141592e17f32)    => b"314159196796878848                      ", 18);
+    check_exact!(f(f32::MAX)          => b"34028234663852885981170418348451692544  ", 39);
+    check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37);
+    check_exact!(f(minf32)            => b"1401298464324817070923729583289916131280", -44);
+
+    // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP
+    check_exact_one!(f(12676506, -102; f32) => b"2",            -23);
+    check_exact_one!(f(12676506, -103; f32) => b"12",           -23);
+    check_exact_one!(f(15445013,   86; f32) => b"119",           34);
+    check_exact_one!(f(13734123, -138; f32) => b"3941",         -34);
+    check_exact_one!(f(12428269, -130; f32) => b"91308",        -32);
+    check_exact_one!(f(15334037, -146; f32) => b"171900",       -36);
+    check_exact_one!(f(11518287,  -41; f32) => b"5237910",       -5);
+    check_exact_one!(f(12584953, -145; f32) => b"28216440",     -36);
+    check_exact_one!(f(15961084, -125; f32) => b"375243281",    -30);
+    check_exact_one!(f(14915817, -146; f32) => b"1672120916",   -36);
+    check_exact_one!(f(10845484, -102; f32) => b"21388945814",  -23);
+    check_exact_one!(f(16431059,  -61; f32) => b"712583594561", -11);
+
+    // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP
+    check_exact_one!(f(16093626,   69; f32) => b"1",             29);
+    check_exact_one!(f( 9983778,   25; f32) => b"34",            15);
+    check_exact_one!(f(12745034,  104; f32) => b"259",           39);
+    check_exact_one!(f(12706553,   72; f32) => b"6001",          29);
+    check_exact_one!(f(11005028,   45; f32) => b"38721",         21);
+    check_exact_one!(f(15059547,   71; f32) => b"355584",        29);
+    check_exact_one!(f(16015691,  -99; f32) => b"2526831",      -22);
+    check_exact_one!(f( 8667859,   56; f32) => b"62458507",      24);
+    check_exact_one!(f(14855922,  -82; f32) => b"307213267",    -17);
+    check_exact_one!(f(14855922,  -83; f32) => b"1536066333",   -17);
+    check_exact_one!(f(10144164, -110; f32) => b"78147796834",  -26);
+    check_exact_one!(f(13248074,   95; f32) => b"524810279937",  36);
+}
+
+pub fn f64_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    // 0.0999999999999999777955395074968691915273...
+    // 0.1000000000000000055511151231257827021181...
+    // 0.1000000000000000333066907387546962127089...
+    check_shortest!(f(0.1f64) => b"1", 0);
+
+    // this example is explicitly mentioned in the paper.
+    // 10^3 * 0.0999999999999999857891452847979962825775...
+    // 10^3 * 0.1 (exact)
+    // 10^3 * 0.1000000000000000142108547152020037174224...
+    check_shortest!(f(100.0f64) => b"1", 3);
+
+    // 0.3333333333333332593184650249895639717578...
+    // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding)
+    // 0.3333333333333333703407674875052180141210...
+    check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0);
+
+    // explicit test case for equally closest representations.
+    // Dragon has its own tie-breaking rule; Grisu should fall back.
+    // 10^1 * 0.1000007629394531027955395074968691915273...
+    // 10^1 * 0.100000762939453125 (exact)
+    // 10^1 * 0.1000007629394531472044604925031308084726...
+    check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1);
+
+    // 10^1 * 0.3141591999999999718085064159822650253772...
+    // 10^1 * 0.3141592000000000162174274009885266423225...
+    // 10^1 * 0.3141592000000000606263483859947882592678...
+    check_shortest!(f(3.141592f64) => b"3141592", 1);
+
+    // 10^18 * 0.314159199999999936
+    // 10^18 * 0.3141592 (exact)
+    // 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
+    check_shortest!(f(f64::ldexp(1.0, 64)) => b"18446744073709552", 20);
+
+    // pathological case: high = 10^23 (exact). tie breaking should always prefer that.
+    // 10^24 * 0.099999999999999974834176
+    // 10^24 * 0.099999999999999991611392
+    // 10^24 * 0.100000000000000008388608
+    check_shortest!(f(1.0e23f64) => b"1", 24);
+
+    // 10^309 * 0.1797693134862315508561243283845062402343...
+    // 10^309 * 0.1797693134862315708145274237317043567980...
+    // 10^309 * 0.1797693134862315907729305190789024733617...
+    check_shortest!(f(f64::MAX) => b"17976931348623157", 309);
+
+    // 10^-307 * 0.2225073858507200889024586876085859887650...
+    // 10^-307 * 0.2225073858507201383090232717332404064219...
+    // 10^-307 * 0.2225073858507201877155878558578948240788...
+    check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307);
+
+    // 10^-323 * 0
+    // 10^-323 * 0.4940656458412465441765687928682213723650...
+    // 10^-323 * 0.9881312916824930883531375857364427447301...
+    let minf64 = f64::ldexp(1.0, -1074);
+    check_shortest!(f(minf64) => b"5", -323);
+}
+
+pub fn f64_exact_sanity_test<F>(mut f: F)
+        where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    let minf64 = f64::ldexp(1.0, -1074);
+
+    check_exact!(f(0.1f64)            => b"1000000000000000055511151231257827021181", 0);
+    check_exact!(f(0.45f64)           => b"4500000000000000111022302462515654042363", 0);
+    check_exact!(f(0.5f64)            => b"5                                       ", 0);
+    check_exact!(f(0.95f64)           => b"9499999999999999555910790149937383830547", 0);
+    check_exact!(f(100.0f64)          => b"1                                       ", 3);
+    check_exact!(f(999.5f64)          => b"9995000000000000000000000000000000000000", 3);
+    check_exact!(f(1.0f64/3.0)        => b"3333333333333333148296162562473909929394", 0);
+    check_exact!(f(3.141592f64)       => b"3141592000000000162174274009885266423225", 1);
+    check_exact!(f(3.141592e17f64)    => b"3141592                                 ", 18);
+    check_exact!(f(1.0e23f64)         => b"99999999999999991611392                 ", 23);
+    check_exact!(f(f64::MAX)          => b"1797693134862315708145274237317043567980", 309);
+    check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307);
+    check_exact!(f(minf64)            => b"4940656458412465441765687928682213723650\
+                                           5980261432476442558568250067550727020875\
+                                           1865299836361635992379796564695445717730\
+                                           9266567103559397963987747960107818781263\
+                                           0071319031140452784581716784898210368871\
+                                           8636056998730723050006387409153564984387\
+                                           3124733972731696151400317153853980741262\
+                                           3856559117102665855668676818703956031062\
+                                           4931945271591492455329305456544401127480\
+                                           1297099995419319894090804165633245247571\
+                                           4786901472678015935523861155013480352649\
+                                           3472019379026810710749170333222684475333\
+                                           5720832431936092382893458368060106011506\
+                                           1698097530783422773183292479049825247307\
+                                           7637592724787465608477820373446969953364\
+                                           7017972677717585125660551199131504891101\
+                                           4510378627381672509558373897335989936648\
+                                           0994116420570263709027924276754456522908\
+                                           7538682506419718265533447265625         ", -323);
+
+    // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP
+    check_exact_one!(f(8511030020275656,  -342; f64) => b"9",                       -87);
+    check_exact_one!(f(5201988407066741,  -824; f64) => b"46",                     -232);
+    check_exact_one!(f(6406892948269899,   237; f64) => b"141",                      88);
+    check_exact_one!(f(8431154198732492,    72; f64) => b"3981",                     38);
+    check_exact_one!(f(6475049196144587,    99; f64) => b"41040",                    46);
+    check_exact_one!(f(8274307542972842,   726; f64) => b"292084",                  235);
+    check_exact_one!(f(5381065484265332,  -456; f64) => b"2891946",                -121);
+    check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718",               -302);
+    check_exact_one!(f(7976538478610756,   376; f64) => b"122770163",               130);
+    check_exact_one!(f(5982403858958067,   377; f64) => b"1841552452",              130);
+    check_exact_one!(f(5536995190630837,    93; f64) => b"54835744350",              44);
+    check_exact_one!(f(7225450889282194,   710; f64) => b"389190181146",            230);
+    check_exact_one!(f(7225450889282194,   709; f64) => b"1945950905732",           230);
+    check_exact_one!(f(8703372741147379,   117; f64) => b"14460958381605",           52);
+    check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531",        -285);
+    check_exact_one!(f(7459803696087692,  -707; f64) => b"1107950772878888",       -196);
+    check_exact_one!(f(6080469016670379,  -381; f64) => b"12345501366327440",       -98);
+    check_exact_one!(f(8385515147034757,   721; f64) => b"925031711960365024",      233);
+    check_exact_one!(f(7514216811389786,  -828; f64) => b"4198047150284889840",    -233);
+    check_exact_one!(f(8397297803260511,  -345; f64) => b"11716315319786511046",    -87);
+    check_exact_one!(f(6733459239310543,   202; f64) => b"432810072844612493629",    77);
+    check_exact_one!(f(8091450587292794,  -473; f64) => b"3317710118160031081518", -126);
+
+    // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP
+    check_exact_one!(f(6567258882077402,   952; f64) => b"3",                       303);
+    check_exact_one!(f(6712731423444934,   535; f64) => b"76",                      177);
+    check_exact_one!(f(6712731423444934,   534; f64) => b"378",                     177);
+    check_exact_one!(f(5298405411573037,  -957; f64) => b"4350",                   -272);
+    check_exact_one!(f(5137311167659507,  -144; f64) => b"23037",                   -27);
+    check_exact_one!(f(6722280709661868,   363; f64) => b"126301",                  126);
+    check_exact_one!(f(5344436398034927,  -169; f64) => b"7142211",                 -35);
+    check_exact_one!(f(8369123604277281,  -853; f64) => b"13934574",               -240);
+    check_exact_one!(f(8995822108487663,  -780; f64) => b"141463449",              -218);
+    check_exact_one!(f(8942832835564782,  -383; f64) => b"4539277920",              -99);
+    check_exact_one!(f(8942832835564782,  -384; f64) => b"22696389598",             -99);
+    check_exact_one!(f(8942832835564782,  -385; f64) => b"113481947988",            -99);
+    check_exact_one!(f(6965949469487146,  -249; f64) => b"7700366561890",           -59);
+    check_exact_one!(f(6965949469487146,  -250; f64) => b"38501832809448",          -59);
+    check_exact_one!(f(6965949469487146,  -251; f64) => b"192509164047238",         -59);
+    check_exact_one!(f(7487252720986826,   548; f64) => b"6898586531774201",        181);
+    check_exact_one!(f(5592117679628511,   164; f64) => b"13076622631878654",        66);
+    check_exact_one!(f(8887055249355788,   665; f64) => b"136052020756121240",      217);
+    check_exact_one!(f(6994187472632449,   690; f64) => b"3592810217475959676",     224);
+    check_exact_one!(f(8797576579012143,   588; f64) => b"89125197712484551899",    193);
+    check_exact_one!(f(7363326733505337,   272; f64) => b"558769757362301140950",    98);
+    check_exact_one!(f(8549497411294502,  -448; f64) => b"1176257830728540379990", -118);
+}
+
+pub fn more_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1,
+                      exp: 0, inclusive: true} => b"1", 18);
+    check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1,
+                      exp: 0, inclusive: false} => b"99999999999999999", 17);
+}
+
+fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
+        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
+              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
+              V: FnMut(usize) -> Decoded {
+    assert!(k <= 1024);
+
+    let mut npassed = 0; // f(x) = Some(g(x))
+    let mut nignored = 0; // f(x) = None
+
+    for i in 0..n {
+        if (i & 0xfffff) == 0 {
+            println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})",
+                     i, n, nignored, npassed, i - nignored - npassed);
+        }
+
+        let decoded = v(i);
+        let mut buf1 = [0; 1024];
+        if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
+            let mut buf2 = [0; 1024];
+            let (len2, e2) = g(&decoded, &mut buf2[..k]);
+            if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
+                npassed += 1;
+            } else {
+                println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
+                         i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
+                                        str::from_utf8(&buf2[..len2]).unwrap(), e2);
+            }
+        } else {
+            nignored += 1;
+        }
+    }
+    println!("{}({}): done, ignored={} passed={} failed={}",
+             func, k, nignored, npassed, n - nignored - npassed);
+    assert!(nignored + npassed == n,
+            "{}({}): {} out of {} values returns an incorrect value!",
+            func, k, n - nignored - npassed, n);
+    (npassed, nignored)
+}
+
+pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
+        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
+              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
+    let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
+    iterate("f32_random_equivalence_test", k, n, f, g, |_| {
+        let i: u32 = f32_range.ind_sample(&mut rng);
+        let x: f32 = unsafe {mem::transmute(i)};
+        decode_finite(x)
+    });
+}
+
+pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
+        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
+              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
+    let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
+    iterate("f64_random_equivalence_test", k, n, f, g, |_| {
+        let i: u64 = f64_range.ind_sample(&mut rng);
+        let x: f64 = unsafe {mem::transmute(i)};
+        decode_finite(x)
+    });
+}
+
+pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
+        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
+              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
+    // so why not simply testing all of them?
+    //
+    // this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
+    // 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), (2121451881, 17643158));
+}
+
+fn to_string_with_parts<F>(mut f: F) -> String
+        where F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a> {
+    let mut buf = [0; 1024];
+    let mut parts = [Part::Zero(0); 16];
+    let formatted = f(&mut buf, &mut parts);
+    let mut ret = vec![0; formatted.len()];
+    assert_eq!(formatted.write(&mut ret), Some(ret.len()));
+    String::from_utf8(ret).unwrap()
+}
+
+pub fn to_shortest_str_test<F>(mut f_: F)
+        where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    use core::num::flt2dec::Sign::*;
+
+    fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String
+            where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+        to_string_with_parts(|buf, parts| to_shortest_str(|d,b| f(d,b), v, sign,
+                                                          frac_digits, upper, buf, parts))
+    }
+
+    let f = &mut f_;
+
+    assert_eq!(to_string(f,  0.0, Minus,        0, false), "0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     0, false), "0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    0, false), "+0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 0, false), "+0");
+    assert_eq!(to_string(f, -0.0, Minus,        0, false), "0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     0, false), "-0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    0, false), "+0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0");
+    assert_eq!(to_string(f,  0.0, Minus,        1,  true), "0.0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     1,  true), "0.0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    1,  true), "+0.0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 1,  true), "+0.0");
+    assert_eq!(to_string(f, -0.0, Minus,        8,  true), "0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     8,  true), "-0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    8,  true), "+0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8,  true), "-0.00000000");
+
+    assert_eq!(to_string(f,  1.0/0.0, Minus,         0, false), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusRaw,      0,  true), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlus,     0, false), "+inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlusRaw,  0,  true), "+inf");
+    assert_eq!(to_string(f,  0.0/0.0, Minus,         0, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusRaw,      1,  true), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlus,     8, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlusRaw, 64,  true), "NaN");
+    assert_eq!(to_string(f, -1.0/0.0, Minus,         0, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusRaw,      1,  true), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlus,     8, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64,  true), "-inf");
+
+    assert_eq!(to_string(f,  3.14, Minus,        0, false), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     0, false), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    0, false), "+3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 0, false), "+3.14");
+    assert_eq!(to_string(f, -3.14, Minus,        0, false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     0, false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    0, false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3.14");
+    assert_eq!(to_string(f,  3.14, Minus,        1,  true), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     2,  true), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    3,  true), "+3.140");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 4,  true), "+3.1400");
+    assert_eq!(to_string(f, -3.14, Minus,        8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8,  true), "-3.14000000");
+
+    assert_eq!(to_string(f, 7.5e-11, Minus,  0, false), "0.000000000075");
+    assert_eq!(to_string(f, 7.5e-11, Minus,  3, false), "0.000000000075");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750");
+
+    assert_eq!(to_string(f, 1.9971e20, Minus, 0, false), "199710000000000000000");
+    assert_eq!(to_string(f, 1.9971e20, Minus, 1, false), "199710000000000000000.0");
+    assert_eq!(to_string(f, 1.9971e20, Minus, 8, false), "199710000000000000000.00000000");
+
+    assert_eq!(to_string(f, f32::MAX, Minus, 0, false), format!("34028235{:0>31}", ""));
+    assert_eq!(to_string(f, f32::MAX, Minus, 1, false), format!("34028235{:0>31}.0", ""));
+    assert_eq!(to_string(f, f32::MAX, Minus, 8, false), format!("34028235{:0>31}.00000000", ""));
+
+    let minf32 = f32::ldexp(1.0, -149);
+    assert_eq!(to_string(f, minf32, Minus,  0, false), format!("0.{:0>44}1", ""));
+    assert_eq!(to_string(f, minf32, Minus, 45, false), format!("0.{:0>44}1", ""));
+    assert_eq!(to_string(f, minf32, Minus, 46, false), format!("0.{:0>44}10", ""));
+
+    assert_eq!(to_string(f, f64::MAX, Minus, 0, false),
+               format!("17976931348623157{:0>292}", ""));
+    assert_eq!(to_string(f, f64::MAX, Minus, 1, false),
+               format!("17976931348623157{:0>292}.0", ""));
+    assert_eq!(to_string(f, f64::MAX, Minus, 8, false),
+               format!("17976931348623157{:0>292}.00000000", ""));
+
+    let minf64 = f64::ldexp(1.0, -1074);
+    assert_eq!(to_string(f, minf64, Minus,   0, false), format!("0.{:0>323}5", ""));
+    assert_eq!(to_string(f, minf64, Minus, 324, false), format!("0.{:0>323}5", ""));
+    assert_eq!(to_string(f, minf64, Minus, 325, false), format!("0.{:0>323}50", ""));
+
+    // very large output
+    assert_eq!(to_string(f, 1.1, Minus, 80000, false), format!("1.1{:0>79999}", ""));
+}
+
+pub fn to_shortest_exp_str_test<F>(mut f_: F)
+        where F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+    use core::num::flt2dec::Sign::*;
+
+    fn to_string<T, F>(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String
+            where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
+        to_string_with_parts(|buf, parts| to_shortest_exp_str(|d,b| f(d,b), v, sign,
+                                                              exp_bounds, upper, buf, parts))
+    }
+
+    let f = &mut f_;
+
+    assert_eq!(to_string(f,  0.0, Minus,        (-4, 16), false), "0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     (-4, 16), false), "0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    (-4, 16), false), "+0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, (-4, 16), false), "+0");
+    assert_eq!(to_string(f, -0.0, Minus,        (-4, 16), false), "0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     (-4, 16), false), "-0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    (-4, 16), false), "+0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, (-4, 16), false), "-0");
+    assert_eq!(to_string(f,  0.0, Minus,        ( 0,  0),  true), "0E0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     ( 0,  0), false), "0e0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    (-9, -5),  true), "+0E0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, ( 5,  9), false), "+0e0");
+    assert_eq!(to_string(f, -0.0, Minus,        ( 0,  0),  true), "0E0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     ( 0,  0), false), "-0e0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    (-9, -5),  true), "+0E0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, ( 5,  9), false), "-0e0");
+
+    assert_eq!(to_string(f,  1.0/0.0, Minus,        (-4, 16), false), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusRaw,     (-4, 16),  true), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlus,    (-4, 16), false), "+inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlusRaw, (-4, 16),  true), "+inf");
+    assert_eq!(to_string(f,  0.0/0.0, Minus,        ( 0,  0), false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusRaw,     ( 0,  0),  true), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlus,    (-9, -5), false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlusRaw, ( 5,  9),  true), "NaN");
+    assert_eq!(to_string(f, -1.0/0.0, Minus,        ( 0,  0), false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusRaw,     ( 0,  0),  true), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlus,    (-9, -5), false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, ( 5,  9),  true), "-inf");
+
+    assert_eq!(to_string(f,  3.14, Minus,        (-4, 16), false), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     (-4, 16), false), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    (-4, 16), false), "+3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, (-4, 16), false), "+3.14");
+    assert_eq!(to_string(f, -3.14, Minus,        (-4, 16), false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     (-4, 16), false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    (-4, 16), false), "-3.14");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, (-4, 16), false), "-3.14");
+    assert_eq!(to_string(f,  3.14, Minus,        ( 0,  0),  true), "3.14E0");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     ( 0,  0), false), "3.14e0");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    (-9, -5),  true), "+3.14E0");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, ( 5,  9), false), "+3.14e0");
+    assert_eq!(to_string(f, -3.14, Minus,        ( 0,  0),  true), "-3.14E0");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     ( 0,  0), false), "-3.14e0");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    (-9, -5),  true), "-3.14E0");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, ( 5,  9), false), "-3.14e0");
+
+    assert_eq!(to_string(f,  0.1, Minus,        (-4, 16), false), "0.1");
+    assert_eq!(to_string(f,  0.1, MinusRaw,     (-4, 16), false), "0.1");
+    assert_eq!(to_string(f,  0.1, MinusPlus,    (-4, 16), false), "+0.1");
+    assert_eq!(to_string(f,  0.1, MinusPlusRaw, (-4, 16), false), "+0.1");
+    assert_eq!(to_string(f, -0.1, Minus,        (-4, 16), false), "-0.1");
+    assert_eq!(to_string(f, -0.1, MinusRaw,     (-4, 16), false), "-0.1");
+    assert_eq!(to_string(f, -0.1, MinusPlus,    (-4, 16), false), "-0.1");
+    assert_eq!(to_string(f, -0.1, MinusPlusRaw, (-4, 16), false), "-0.1");
+    assert_eq!(to_string(f,  0.1, Minus,        ( 0,  0),  true), "1E-1");
+    assert_eq!(to_string(f,  0.1, MinusRaw,     ( 0,  0), false), "1e-1");
+    assert_eq!(to_string(f,  0.1, MinusPlus,    (-9, -5),  true), "+1E-1");
+    assert_eq!(to_string(f,  0.1, MinusPlusRaw, ( 5,  9), false), "+1e-1");
+    assert_eq!(to_string(f, -0.1, Minus,        ( 0,  0),  true), "-1E-1");
+    assert_eq!(to_string(f, -0.1, MinusRaw,     ( 0,  0), false), "-1e-1");
+    assert_eq!(to_string(f, -0.1, MinusPlus,    (-9, -5),  true), "-1E-1");
+    assert_eq!(to_string(f, -0.1, MinusPlusRaw, ( 5,  9), false), "-1e-1");
+
+    assert_eq!(to_string(f, 7.5e-11, Minus, ( -4, 16), false), "7.5e-11");
+    assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075");
+    assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11");
+
+    assert_eq!(to_string(f, 1.9971e20, Minus, ( -4, 16), false), "1.9971e20");
+    assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000");
+    assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20");
+
+    // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here
+    assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23");
+    assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000");
+    assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23");
+
+    assert_eq!(to_string(f, f32::MAX, Minus, ( -4, 16), false), "3.4028235e38");
+    assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
+    assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
+
+    let minf32 = f32::ldexp(1.0, -149);
+    assert_eq!(to_string(f, minf32, Minus, ( -4, 16), false), "1e-45");
+    assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
+    assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
+
+    assert_eq!(to_string(f, f64::MAX, Minus, (  -4,  16), false),
+               "1.7976931348623157e308");
+    assert_eq!(to_string(f, f64::MAX, Minus, (-308, 309), false),
+               format!("17976931348623157{:0>292}", ""));
+    assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false),
+               "1.7976931348623157e308");
+
+    let minf64 = f64::ldexp(1.0, -1074);
+    assert_eq!(to_string(f, minf64, Minus, (  -4,  16), false), "5e-324");
+    assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
+    assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
+
+    assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1");
+}
+
+pub fn to_exact_exp_str_test<F>(mut f_: F)
+        where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    use core::num::flt2dec::Sign::*;
+
+    fn to_string<T, F>(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String
+            where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+        to_string_with_parts(|buf, parts| to_exact_exp_str(|d,b,l| f(d,b,l), v, sign,
+                                                           ndigits, upper, buf, parts))
+    }
+
+    let f = &mut f_;
+
+    assert_eq!(to_string(f,  0.0, Minus,        1,  true), "0E0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     1, false), "0e0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    1,  true), "+0E0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 1, false), "+0e0");
+    assert_eq!(to_string(f, -0.0, Minus,        1,  true), "0E0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     1, false), "-0e0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    1,  true), "+0E0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 1, false), "-0e0");
+    assert_eq!(to_string(f,  0.0, Minus,        2,  true), "0.0E0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     2, false), "0.0e0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    2,  true), "+0.0E0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 2, false), "+0.0e0");
+    assert_eq!(to_string(f, -0.0, Minus,        8,  true), "0.0000000E0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     8, false), "-0.0000000e0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    8,  true), "+0.0000000E0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0");
+
+    assert_eq!(to_string(f,  1.0/0.0, Minus,         1, false), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusRaw,      1,  true), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlus,     1, false), "+inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlusRaw,  1,  true), "+inf");
+    assert_eq!(to_string(f,  0.0/0.0, Minus,         8, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusRaw,      8,  true), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlus,     8, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlusRaw,  8,  true), "NaN");
+    assert_eq!(to_string(f, -1.0/0.0, Minus,        64, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusRaw,     64,  true), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlus,    64, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64,  true), "-inf");
+
+    assert_eq!(to_string(f,  3.14, Minus,        1,  true), "3E0");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     1, false), "3e0");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    1,  true), "+3E0");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 1, false), "+3e0");
+    assert_eq!(to_string(f, -3.14, Minus,        2,  true), "-3.1E0");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     2, false), "-3.1e0");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    2,  true), "-3.1E0");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 2, false), "-3.1e0");
+    assert_eq!(to_string(f,  3.14, Minus,        3,  true), "3.14E0");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     3, false), "3.14e0");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    3,  true), "+3.14E0");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 3, false), "+3.14e0");
+    assert_eq!(to_string(f, -3.14, Minus,        4,  true), "-3.140E0");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     4, false), "-3.140e0");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    4,  true), "-3.140E0");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 4, false), "-3.140e0");
+
+    assert_eq!(to_string(f,  0.195, Minus,        1, false), "2e-1");
+    assert_eq!(to_string(f,  0.195, MinusRaw,     1,  true), "2E-1");
+    assert_eq!(to_string(f,  0.195, MinusPlus,    1, false), "+2e-1");
+    assert_eq!(to_string(f,  0.195, MinusPlusRaw, 1,  true), "+2E-1");
+    assert_eq!(to_string(f, -0.195, Minus,        2, false), "-2.0e-1");
+    assert_eq!(to_string(f, -0.195, MinusRaw,     2,  true), "-2.0E-1");
+    assert_eq!(to_string(f, -0.195, MinusPlus,    2, false), "-2.0e-1");
+    assert_eq!(to_string(f, -0.195, MinusPlusRaw, 2,  true), "-2.0E-1");
+    assert_eq!(to_string(f,  0.195, Minus,        3, false), "1.95e-1");
+    assert_eq!(to_string(f,  0.195, MinusRaw,     3,  true), "1.95E-1");
+    assert_eq!(to_string(f,  0.195, MinusPlus,    3, false), "+1.95e-1");
+    assert_eq!(to_string(f,  0.195, MinusPlusRaw, 3,  true), "+1.95E-1");
+    assert_eq!(to_string(f, -0.195, Minus,        4, false), "-1.950e-1");
+    assert_eq!(to_string(f, -0.195, MinusRaw,     4,  true), "-1.950E-1");
+    assert_eq!(to_string(f, -0.195, MinusPlus,    4, false), "-1.950e-1");
+    assert_eq!(to_string(f, -0.195, MinusPlusRaw, 4,  true), "-1.950E-1");
+
+    assert_eq!(to_string(f, 9.5, Minus,  1, false), "1e1");
+    assert_eq!(to_string(f, 9.5, Minus,  2, false), "9.5e0");
+    assert_eq!(to_string(f, 9.5, Minus,  3, false), "9.50e0");
+    assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0");
+
+    assert_eq!(to_string(f, 1.0e25, Minus,  1, false), "1e25");
+    assert_eq!(to_string(f, 1.0e25, Minus,  2, false), "1.0e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25");
+    assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25");
+
+    assert_eq!(to_string(f, 1.0e-6, Minus,  1, false), "1e-6");
+    assert_eq!(to_string(f, 1.0e-6, Minus,  2, false), "1.0e-6");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 40, false),
+               "9.999999999999999547481118258862586856139e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 50, false),
+               "9.9999999999999995474811182588625868561393872369081e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 60, false),
+               "9.99999999999999954748111825886258685613938723690807819366455e-7");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 70, false),
+               "9.999999999999999547481118258862586856139387236908078193664550781250000e-7");
+
+    assert_eq!(to_string(f, f32::MAX, Minus,  1, false), "3e38");
+    assert_eq!(to_string(f, f32::MAX, Minus,  2, false), "3.4e38");
+    assert_eq!(to_string(f, f32::MAX, Minus,  4, false), "3.403e38");
+    assert_eq!(to_string(f, f32::MAX, Minus,  8, false), "3.4028235e38");
+    assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38");
+    assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38");
+    assert_eq!(to_string(f, f32::MAX, Minus, 64, false),
+               "3.402823466385288598117041834845169254400000000000000000000000000e38");
+
+    let minf32 = f32::ldexp(1.0, -149);
+    assert_eq!(to_string(f, minf32, Minus,   1, false), "1e-45");
+    assert_eq!(to_string(f, minf32, Minus,   2, false), "1.4e-45");
+    assert_eq!(to_string(f, minf32, Minus,   4, false), "1.401e-45");
+    assert_eq!(to_string(f, minf32, Minus,   8, false), "1.4012985e-45");
+    assert_eq!(to_string(f, minf32, Minus,  16, false), "1.401298464324817e-45");
+    assert_eq!(to_string(f, minf32, Minus,  32, false), "1.4012984643248170709237295832899e-45");
+    assert_eq!(to_string(f, minf32, Minus,  64, false),
+               "1.401298464324817070923729583289916131280261941876515771757068284e-45");
+    assert_eq!(to_string(f, minf32, Minus, 128, false),
+               "1.401298464324817070923729583289916131280261941876515771757068283\
+                 8897910826858606014866381883621215820312500000000000000000000000e-45");
+
+    assert_eq!(to_string(f, f64::MAX, Minus,   1, false), "2e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,   2, false), "1.8e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,   4, false), "1.798e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,   8, false), "1.7976931e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,  16, false), "1.797693134862316e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,  32, false), "1.7976931348623157081452742373170e308");
+    assert_eq!(to_string(f, f64::MAX, Minus,  64, false),
+               "1.797693134862315708145274237317043567980705675258449965989174768e308");
+    assert_eq!(to_string(f, f64::MAX, Minus, 128, false),
+               "1.797693134862315708145274237317043567980705675258449965989174768\
+                 0315726078002853876058955863276687817154045895351438246423432133e308");
+    assert_eq!(to_string(f, f64::MAX, Minus, 256, false),
+               "1.797693134862315708145274237317043567980705675258449965989174768\
+                 0315726078002853876058955863276687817154045895351438246423432132\
+                 6889464182768467546703537516986049910576551282076245490090389328\
+                 9440758685084551339423045832369032229481658085593321233482747978e308");
+    assert_eq!(to_string(f, f64::MAX, Minus, 512, false),
+               "1.797693134862315708145274237317043567980705675258449965989174768\
+                 0315726078002853876058955863276687817154045895351438246423432132\
+                 6889464182768467546703537516986049910576551282076245490090389328\
+                 9440758685084551339423045832369032229481658085593321233482747978\
+                 2620414472316873817718091929988125040402618412485836800000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000e308");
+
+    // okay, this is becoming tough. fortunately for us, this is almost the worst case.
+    let minf64 = f64::ldexp(1.0, -1074);
+    assert_eq!(to_string(f, minf64, Minus,    1, false), "5e-324");
+    assert_eq!(to_string(f, minf64, Minus,    2, false), "4.9e-324");
+    assert_eq!(to_string(f, minf64, Minus,    4, false), "4.941e-324");
+    assert_eq!(to_string(f, minf64, Minus,    8, false), "4.9406565e-324");
+    assert_eq!(to_string(f, minf64, Minus,   16, false), "4.940656458412465e-324");
+    assert_eq!(to_string(f, minf64, Minus,   32, false), "4.9406564584124654417656879286822e-324");
+    assert_eq!(to_string(f, minf64, Minus,   64, false),
+               "4.940656458412465441765687928682213723650598026143247644255856825e-324");
+    assert_eq!(to_string(f, minf64, Minus,  128, false),
+               "4.940656458412465441765687928682213723650598026143247644255856825\
+                 0067550727020875186529983636163599237979656469544571773092665671e-324");
+    assert_eq!(to_string(f, minf64, Minus,  256, false),
+               "4.940656458412465441765687928682213723650598026143247644255856825\
+                 0067550727020875186529983636163599237979656469544571773092665671\
+                 0355939796398774796010781878126300713190311404527845817167848982\
+                 1036887186360569987307230500063874091535649843873124733972731696e-324");
+    assert_eq!(to_string(f, minf64, Minus,  512, false),
+               "4.940656458412465441765687928682213723650598026143247644255856825\
+                 0067550727020875186529983636163599237979656469544571773092665671\
+                 0355939796398774796010781878126300713190311404527845817167848982\
+                 1036887186360569987307230500063874091535649843873124733972731696\
+                 1514003171538539807412623856559117102665855668676818703956031062\
+                 4931945271591492455329305456544401127480129709999541931989409080\
+                 4165633245247571478690147267801593552386115501348035264934720193\
+                 7902681071074917033322268447533357208324319360923828934583680601e-324");
+    assert_eq!(to_string(f, minf64, Minus, 1024, false),
+               "4.940656458412465441765687928682213723650598026143247644255856825\
+                 0067550727020875186529983636163599237979656469544571773092665671\
+                 0355939796398774796010781878126300713190311404527845817167848982\
+                 1036887186360569987307230500063874091535649843873124733972731696\
+                 1514003171538539807412623856559117102665855668676818703956031062\
+                 4931945271591492455329305456544401127480129709999541931989409080\
+                 4165633245247571478690147267801593552386115501348035264934720193\
+                 7902681071074917033322268447533357208324319360923828934583680601\
+                 0601150616980975307834227731832924790498252473077637592724787465\
+                 6084778203734469699533647017972677717585125660551199131504891101\
+                 4510378627381672509558373897335989936648099411642057026370902792\
+                 4276754456522908753868250641971826553344726562500000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000\
+                 0000000000000000000000000000000000000000000000000000000000000000e-324");
+
+    // very large output
+    assert_eq!(to_string(f, 0.0,     Minus, 80000, false), format!("0.{:0>79999}e0", ""));
+    assert_eq!(to_string(f, 1.0e1,   Minus, 80000, false), format!("1.{:0>79999}e1", ""));
+    assert_eq!(to_string(f, 1.0e0,   Minus, 80000, false), format!("1.{:0>79999}e0", ""));
+    assert_eq!(to_string(f, 1.0e-1,  Minus, 80000, false),
+               format!("1.000000000000000055511151231257827021181583404541015625{:0>79945}\
+                        e-1", ""));
+    assert_eq!(to_string(f, 1.0e-20, Minus, 80000, false),
+               format!("9.999999999999999451532714542095716517295037027873924471077157760\
+                         66783064379706047475337982177734375{:0>79901}e-21", ""));
+}
+
+pub fn to_exact_fixed_str_test<F>(mut f_: F)
+        where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+    use core::num::flt2dec::Sign::*;
+
+    fn to_string<T, F>(f: &mut F, v: T, sign: Sign, frac_digits: usize, upper: bool) -> String
+            where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
+        to_string_with_parts(|buf, parts| to_exact_fixed_str(|d,b,l| f(d,b,l), v, sign,
+                                                             frac_digits, upper, buf, parts))
+    }
+
+    let f = &mut f_;
+
+    assert_eq!(to_string(f,  0.0, Minus,        0, false), "0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     0, false), "0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    0, false), "+0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 0, false), "+0");
+    assert_eq!(to_string(f, -0.0, Minus,        0, false), "0");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     0, false), "-0");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    0, false), "+0");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0, false), "-0");
+    assert_eq!(to_string(f,  0.0, Minus,        1,  true), "0.0");
+    assert_eq!(to_string(f,  0.0, MinusRaw,     1,  true), "0.0");
+    assert_eq!(to_string(f,  0.0, MinusPlus,    1,  true), "+0.0");
+    assert_eq!(to_string(f,  0.0, MinusPlusRaw, 1,  true), "+0.0");
+    assert_eq!(to_string(f, -0.0, Minus,        8,  true), "0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusRaw,     8,  true), "-0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusPlus,    8,  true), "+0.00000000");
+    assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8,  true), "-0.00000000");
+
+    assert_eq!(to_string(f,  1.0/0.0, Minus,         0, false), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusRaw,      1,  true), "inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlus,     8, false), "+inf");
+    assert_eq!(to_string(f,  1.0/0.0, MinusPlusRaw, 64,  true), "+inf");
+    assert_eq!(to_string(f,  0.0/0.0, Minus,         0, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusRaw,      1,  true), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlus,     8, false), "NaN");
+    assert_eq!(to_string(f,  0.0/0.0, MinusPlusRaw, 64,  true), "NaN");
+    assert_eq!(to_string(f, -1.0/0.0, Minus,         0, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusRaw,      1,  true), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlus,     8, false), "-inf");
+    assert_eq!(to_string(f, -1.0/0.0, MinusPlusRaw, 64,  true), "-inf");
+
+    assert_eq!(to_string(f,  3.14, Minus,        0, false), "3");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     0, false), "3");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    0, false), "+3");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 0, false), "+3");
+    assert_eq!(to_string(f, -3.14, Minus,        0, false), "-3");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     0, false), "-3");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    0, false), "-3");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0, false), "-3");
+    assert_eq!(to_string(f,  3.14, Minus,        1,  true), "3.1");
+    assert_eq!(to_string(f,  3.14, MinusRaw,     2,  true), "3.14");
+    assert_eq!(to_string(f,  3.14, MinusPlus,    3,  true), "+3.140");
+    assert_eq!(to_string(f,  3.14, MinusPlusRaw, 4,  true), "+3.1400");
+    assert_eq!(to_string(f, -3.14, Minus,        8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusRaw,     8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusPlus,    8,  true), "-3.14000000");
+    assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8,  true), "-3.14000000");
+
+    assert_eq!(to_string(f,  0.195, Minus,        0, false), "0");
+    assert_eq!(to_string(f,  0.195, MinusRaw,     0, false), "0");
+    assert_eq!(to_string(f,  0.195, MinusPlus,    0, false), "+0");
+    assert_eq!(to_string(f,  0.195, MinusPlusRaw, 0, false), "+0");
+    assert_eq!(to_string(f, -0.195, Minus,        0, false), "-0");
+    assert_eq!(to_string(f, -0.195, MinusRaw,     0, false), "-0");
+    assert_eq!(to_string(f, -0.195, MinusPlus,    0, false), "-0");
+    assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0, false), "-0");
+    assert_eq!(to_string(f,  0.195, Minus,        1,  true), "0.2");
+    assert_eq!(to_string(f,  0.195, MinusRaw,     2,  true), "0.20");
+    assert_eq!(to_string(f,  0.195, MinusPlus,    3,  true), "+0.195");
+    assert_eq!(to_string(f,  0.195, MinusPlusRaw, 4,  true), "+0.1950");
+    assert_eq!(to_string(f, -0.195, Minus,        5,  true), "-0.19500");
+    assert_eq!(to_string(f, -0.195, MinusRaw,     6,  true), "-0.195000");
+    assert_eq!(to_string(f, -0.195, MinusPlus,    7,  true), "-0.1950000");
+    assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8,  true), "-0.19500000");
+
+    assert_eq!(to_string(f, 999.5, Minus,  0, false), "1000");
+    assert_eq!(to_string(f, 999.5, Minus,  1, false), "999.5");
+    assert_eq!(to_string(f, 999.5, Minus,  2, false), "999.50");
+    assert_eq!(to_string(f, 999.5, Minus,  3, false), "999.500");
+    assert_eq!(to_string(f, 999.5, Minus, 30, false), "999.500000000000000000000000000000");
+
+    assert_eq!(to_string(f, 0.5, Minus, 0, false), "1");
+    assert_eq!(to_string(f, 0.5, Minus, 1, false), "0.5");
+    assert_eq!(to_string(f, 0.5, Minus, 2, false), "0.50");
+    assert_eq!(to_string(f, 0.5, Minus, 3, false), "0.500");
+
+    assert_eq!(to_string(f, 0.95, Minus,  0, false), "1");
+    assert_eq!(to_string(f, 0.95, Minus,  1, false), "0.9"); // because it really is less than 0.95
+    assert_eq!(to_string(f, 0.95, Minus,  2, false), "0.95");
+    assert_eq!(to_string(f, 0.95, Minus,  3, false), "0.950");
+    assert_eq!(to_string(f, 0.95, Minus, 10, false), "0.9500000000");
+    assert_eq!(to_string(f, 0.95, Minus, 30, false), "0.949999999999999955591079014994");
+
+    assert_eq!(to_string(f, 0.095, Minus,  0, false), "0");
+    assert_eq!(to_string(f, 0.095, Minus,  1, false), "0.1");
+    assert_eq!(to_string(f, 0.095, Minus,  2, false), "0.10");
+    assert_eq!(to_string(f, 0.095, Minus,  3, false), "0.095");
+    assert_eq!(to_string(f, 0.095, Minus,  4, false), "0.0950");
+    assert_eq!(to_string(f, 0.095, Minus, 10, false), "0.0950000000");
+    assert_eq!(to_string(f, 0.095, Minus, 30, false), "0.095000000000000001110223024625");
+
+    assert_eq!(to_string(f, 0.0095, Minus,  0, false), "0");
+    assert_eq!(to_string(f, 0.0095, Minus,  1, false), "0.0");
+    assert_eq!(to_string(f, 0.0095, Minus,  2, false), "0.01");
+    assert_eq!(to_string(f, 0.0095, Minus,  3, false), "0.009"); // really is less than 0.0095
+    assert_eq!(to_string(f, 0.0095, Minus,  4, false), "0.0095");
+    assert_eq!(to_string(f, 0.0095, Minus,  5, false), "0.00950");
+    assert_eq!(to_string(f, 0.0095, Minus, 10, false), "0.0095000000");
+    assert_eq!(to_string(f, 0.0095, Minus, 30, false), "0.009499999999999999764077607267");
+
+    assert_eq!(to_string(f, 7.5e-11, Minus,  0, false), "0");
+    assert_eq!(to_string(f, 7.5e-11, Minus,  3, false), "0.000");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 10, false), "0.0000000001");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 11, false), "0.00000000007"); // ditto
+    assert_eq!(to_string(f, 7.5e-11, Minus, 12, false), "0.000000000075");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 13, false), "0.0000000000750");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 20, false), "0.00000000007500000000");
+    assert_eq!(to_string(f, 7.5e-11, Minus, 30, false), "0.000000000074999999999999999501");
+
+    assert_eq!(to_string(f, 1.0e25, Minus, 0, false), "10000000000000000905969664");
+    assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "10000000000000000905969664.0");
+    assert_eq!(to_string(f, 1.0e25, Minus, 3, false), "10000000000000000905969664.000");
+
+    assert_eq!(to_string(f, 1.0e-6, Minus,  0, false), "0");
+    assert_eq!(to_string(f, 1.0e-6, Minus,  3, false), "0.000");
+    assert_eq!(to_string(f, 1.0e-6, Minus,  6, false), "0.000001");
+    assert_eq!(to_string(f, 1.0e-6, Minus,  9, false), "0.000001000");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 12, false), "0.000001000000");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 22, false), "0.0000010000000000000000");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 23, false), "0.00000099999999999999995");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 24, false), "0.000000999999999999999955");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 25, false), "0.0000009999999999999999547");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 35, false), "0.00000099999999999999995474811182589");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 45, false),
+               "0.000000999999999999999954748111825886258685614");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 55, false),
+               "0.0000009999999999999999547481118258862586856139387236908");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 65, false),
+               "0.00000099999999999999995474811182588625868561393872369080781936646");
+    assert_eq!(to_string(f, 1.0e-6, Minus, 75, false),
+               "0.000000999999999999999954748111825886258685613938723690807819366455078125000");
+
+    assert_eq!(to_string(f, f32::MAX, Minus, 0, false),
+               "340282346638528859811704183484516925440");
+    assert_eq!(to_string(f, f32::MAX, Minus, 1, false),
+               "340282346638528859811704183484516925440.0");
+    assert_eq!(to_string(f, f32::MAX, Minus, 2, false),
+               "340282346638528859811704183484516925440.00");
+
+    let minf32 = f32::ldexp(1.0, -149);
+    assert_eq!(to_string(f, minf32, Minus,   0, false), "0");
+    assert_eq!(to_string(f, minf32, Minus,   1, false), "0.0");
+    assert_eq!(to_string(f, minf32, Minus,   2, false), "0.00");
+    assert_eq!(to_string(f, minf32, Minus,   4, false), "0.0000");
+    assert_eq!(to_string(f, minf32, Minus,   8, false), "0.00000000");
+    assert_eq!(to_string(f, minf32, Minus,  16, false), "0.0000000000000000");
+    assert_eq!(to_string(f, minf32, Minus,  32, false), "0.00000000000000000000000000000000");
+    assert_eq!(to_string(f, minf32, Minus,  64, false),
+               "0.0000000000000000000000000000000000000000000014012984643248170709");
+    assert_eq!(to_string(f, minf32, Minus, 128, false),
+               "0.0000000000000000000000000000000000000000000014012984643248170709\
+                  2372958328991613128026194187651577175706828388979108268586060149");
+    assert_eq!(to_string(f, minf32, Minus, 256, false),
+               "0.0000000000000000000000000000000000000000000014012984643248170709\
+                  2372958328991613128026194187651577175706828388979108268586060148\
+                  6638188362121582031250000000000000000000000000000000000000000000\
+                  0000000000000000000000000000000000000000000000000000000000000000");
+
+    assert_eq!(to_string(f, f64::MAX, Minus, 0, false),
+               "1797693134862315708145274237317043567980705675258449965989174768\
+                0315726078002853876058955863276687817154045895351438246423432132\
+                6889464182768467546703537516986049910576551282076245490090389328\
+                9440758685084551339423045832369032229481658085593321233482747978\
+                26204144723168738177180919299881250404026184124858368");
+    assert_eq!(to_string(f, f64::MAX, Minus, 10, false),
+               "1797693134862315708145274237317043567980705675258449965989174768\
+                0315726078002853876058955863276687817154045895351438246423432132\
+                6889464182768467546703537516986049910576551282076245490090389328\
+                9440758685084551339423045832369032229481658085593321233482747978\
+                26204144723168738177180919299881250404026184124858368.0000000000");
+
+    let minf64 = f64::ldexp(1.0, -1074);
+    assert_eq!(to_string(f, minf64, Minus, 0, false), "0");
+    assert_eq!(to_string(f, minf64, Minus, 1, false), "0.0");
+    assert_eq!(to_string(f, minf64, Minus, 10, false), "0.0000000000");
+    assert_eq!(to_string(f, minf64, Minus, 100, false),
+               "0.0000000000000000000000000000000000000000000000000000000000000000\
+                  000000000000000000000000000000000000");
+    assert_eq!(to_string(f, minf64, Minus, 1000, false),
+               "0.0000000000000000000000000000000000000000000000000000000000000000\
+                  0000000000000000000000000000000000000000000000000000000000000000\
+                  0000000000000000000000000000000000000000000000000000000000000000\
+                  0000000000000000000000000000000000000000000000000000000000000000\
+                  0000000000000000000000000000000000000000000000000000000000000000\
+                  0004940656458412465441765687928682213723650598026143247644255856\
+                  8250067550727020875186529983636163599237979656469544571773092665\
+                  6710355939796398774796010781878126300713190311404527845817167848\
+                  9821036887186360569987307230500063874091535649843873124733972731\
+                  6961514003171538539807412623856559117102665855668676818703956031\
+                  0624931945271591492455329305456544401127480129709999541931989409\
+                  0804165633245247571478690147267801593552386115501348035264934720\
+                  1937902681071074917033322268447533357208324319360923828934583680\
+                  6010601150616980975307834227731832924790498252473077637592724787\
+                  4656084778203734469699533647017972677717585125660551199131504891\
+                  1014510378627381672509558373897335989937");
+
+    // very large output
+    assert_eq!(to_string(f, 0.0,     Minus, 80000, false), format!("0.{:0>80000}", ""));
+    assert_eq!(to_string(f, 1.0e1,   Minus, 80000, false), format!("10.{:0>80000}", ""));
+    assert_eq!(to_string(f, 1.0e0,   Minus, 80000, false), format!("1.{:0>80000}", ""));
+    assert_eq!(to_string(f, 1.0e-1,  Minus, 80000, false),
+               format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", ""));
+    assert_eq!(to_string(f, 1.0e-20, Minus, 80000, false),
+               format!("0.0000000000000000000099999999999999994515327145420957165172950370\
+                          2787392447107715776066783064379706047475337982177734375{:0>79881}", ""));
+}
+
diff --git a/src/libcoretest/num/flt2dec/strategy/dragon.rs b/src/libcoretest/num/flt2dec/strategy/dragon.rs
new file mode 100644
index 00000000000..f2397f6b480
--- /dev/null
+++ b/src/libcoretest/num/flt2dec/strategy/dragon.rs
@@ -0,0 +1,117 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::prelude::v1::*;
+use std::{i16, f64};
+use super::super::*;
+use core::num::flt2dec::*;
+use core::num::flt2dec::bignum::Big32x36 as Big;
+use core::num::flt2dec::strategy::dragon::*;
+
+#[test]
+fn test_mul_pow10() {
+    let mut prevpow10 = Big::from_small(1);
+    for i in 1..340 {
+        let mut curpow10 = Big::from_small(1);
+        mul_pow10(&mut curpow10, i);
+        assert_eq!(curpow10, *prevpow10.clone().mul_small(10));
+        prevpow10 = curpow10;
+    }
+}
+
+#[test]
+fn shortest_sanity_test() {
+    f64_shortest_sanity_test(format_shortest);
+    f32_shortest_sanity_test(format_shortest);
+    more_shortest_sanity_test(format_shortest);
+}
+
+#[test]
+fn exact_sanity_test() {
+    f64_exact_sanity_test(format_exact);
+    f32_exact_sanity_test(format_exact);
+}
+
+#[bench]
+fn bench_small_shortest(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; MAX_SIG_DIGITS];
+    b.iter(|| format_shortest(&decoded, &mut buf));
+}
+
+#[bench]
+fn bench_big_shortest(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; MAX_SIG_DIGITS];
+    b.iter(|| format_shortest(&decoded, &mut buf));
+}
+
+#[bench]
+fn bench_small_exact_3(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 3];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_3(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 3];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_small_exact_12(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 12];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_12(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 12];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_small_exact_inf(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 1024];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_inf(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 1024];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[test]
+fn test_to_shortest_str() {
+    to_shortest_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_shortest_exp_str() {
+    to_shortest_exp_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_exact_exp_str() {
+    to_exact_exp_str_test(format_exact);
+}
+
+#[test]
+fn test_to_exact_fixed_str() {
+    to_exact_fixed_str_test(format_exact);
+}
+
diff --git a/src/libcoretest/num/flt2dec/strategy/grisu.rs b/src/libcoretest/num/flt2dec/strategy/grisu.rs
new file mode 100644
index 00000000000..e5b8a9dcc38
--- /dev/null
+++ b/src/libcoretest/num/flt2dec/strategy/grisu.rs
@@ -0,0 +1,177 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::{i16, f64};
+use super::super::*;
+use core::num::flt2dec::*;
+use core::num::flt2dec::strategy::grisu::*;
+
+#[test]
+fn test_cached_power() {
+    assert_eq!(CACHED_POW10.first().unwrap().1, CACHED_POW10_FIRST_E);
+    assert_eq!(CACHED_POW10.last().unwrap().1, CACHED_POW10_LAST_E);
+
+    for e in -1137..961 { // full range for f64
+        let low = ALPHA - e - 64;
+        let high = GAMMA - e - 64;
+        let (_k, cached) = cached_power(low, high);
+        assert!(low <= cached.e && cached.e <= high,
+                "cached_power({}, {}) = {:?} is incorrect", low, high, cached);
+    }
+}
+
+#[test]
+fn test_max_pow10_no_more_than() {
+    let mut prevtenk = 1;
+    for k in 1..10 {
+        let tenk = prevtenk * 10;
+        assert_eq!(max_pow10_no_more_than(tenk - 1), (k - 1, prevtenk));
+        assert_eq!(max_pow10_no_more_than(tenk), (k, tenk));
+        prevtenk = tenk;
+    }
+}
+
+
+#[test]
+fn shortest_sanity_test() {
+    f64_shortest_sanity_test(format_shortest);
+    f32_shortest_sanity_test(format_shortest);
+    more_shortest_sanity_test(format_shortest);
+}
+
+#[test]
+fn shortest_random_equivalence_test() {
+    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+    f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
+    f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
+}
+
+#[test] #[ignore] // it is too expensive
+fn shortest_f32_exhaustive_equivalence_test() {
+    // it is hard to directly test the optimality of the output, but we can at least test if
+    // two different algorithms agree to each other.
+    //
+    // 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=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);
+}
+
+#[test] #[ignore] // it is too expensive
+fn shortest_f64_hard_random_equivalence_test() {
+    // this again probably has to use appropriate rustc flags.
+
+    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
+    f64_random_equivalence_test(format_shortest_opt, fallback,
+                                         MAX_SIG_DIGITS, 100_000_000);
+}
+
+#[test]
+fn exact_sanity_test() {
+    f64_exact_sanity_test(format_exact);
+    f32_exact_sanity_test(format_exact);
+}
+
+#[test]
+fn exact_f32_random_equivalence_test() {
+    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
+    for k in 1..21 {
+        f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
+                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
+    }
+}
+
+#[test]
+fn exact_f64_random_equivalence_test() {
+    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
+    for k in 1..21 {
+        f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
+                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
+    }
+}
+
+#[bench]
+fn bench_small_shortest(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; MAX_SIG_DIGITS];
+    b.iter(|| format_shortest(&decoded, &mut buf));
+}
+
+#[bench]
+fn bench_big_shortest(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; MAX_SIG_DIGITS];
+    b.iter(|| format_shortest(&decoded, &mut buf));
+}
+
+#[bench]
+fn bench_small_exact_3(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 3];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_3(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 3];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_small_exact_12(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 12];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_12(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 12];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_small_exact_inf(b: &mut Bencher) {
+    let decoded = decode_finite(3.141592f64);
+    let mut buf = [0; 1024];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[bench]
+fn bench_big_exact_inf(b: &mut Bencher) {
+    let decoded = decode_finite(f64::MAX);
+    let mut buf = [0; 1024];
+    b.iter(|| format_exact(&decoded, &mut buf, i16::MIN));
+}
+
+#[test]
+fn test_to_shortest_str() {
+    to_shortest_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_shortest_exp_str() {
+    to_shortest_exp_str_test(format_shortest);
+}
+
+#[test]
+fn test_to_exact_exp_str() {
+    to_exact_exp_str_test(format_exact);
+}
+
+#[test]
+fn test_to_exact_fixed_str() {
+    to_exact_fixed_str_test(format_exact);
+}
+
diff --git a/src/libcoretest/num/mod.rs b/src/libcoretest/num/mod.rs
index 0ea9f8afb4e..998f4b21ece 100644
--- a/src/libcoretest/num/mod.rs
+++ b/src/libcoretest/num/mod.rs
@@ -29,6 +29,8 @@ mod u16;
 mod u32;
 mod u64;
 
+mod flt2dec;
+
 /// Helper function for testing numeric operations
 pub fn test_num<T>(ten: T, two: T) where
     T: PartialEq