diff -Nru rust-ryu-0.2.7/benches/bench.rs rust-ryu-1.0.0/benches/bench.rs --- rust-ryu-0.2.7/benches/bench.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-ryu-1.0.0/benches/bench.rs 2019-06-11 21:13:49.000000000 +0000 @@ -0,0 +1,58 @@ +// cargo bench + +#![feature(test)] + +extern crate ryu; +extern crate test; + +macro_rules! benches { + ($($name:ident($value:expr),)*) => { + mod bench_ryu { + use test::{Bencher, black_box}; + $( + #[bench] + fn $name(b: &mut Bencher) { + use ryu; + + let mut buf = ryu::Buffer::new(); + + b.iter(move || { + let value = black_box($value); + let formatted = buf.format_finite(value); + black_box(formatted); + }); + } + )* + } + + mod bench_std_fmt { + use test::{Bencher, black_box}; + $( + #[bench] + fn $name(b: &mut Bencher) { + use std::io::Write; + + let mut buf = Vec::with_capacity(20); + + b.iter(|| { + buf.clear(); + let value = black_box($value); + write!(&mut buf, "{}", value).unwrap(); + black_box(buf.as_slice()); + }); + } + )* + } + } +} + +benches!( + bench_0_f64(0f64), + bench_short_f64(0.1234f64), + bench_e_f64(2.718281828459045f64), + bench_max_f64(::std::f64::MAX), + bench_0_f32(0f32), + bench_short_f32(0.1234f32), + bench_e_f32(2.718281828459045f32), + bench_max_f32(::std::f32::MAX), +); diff -Nru rust-ryu-0.2.7/benchmark/benchmark.rs rust-ryu-1.0.0/benchmark/benchmark.rs --- rust-ryu-0.2.7/benchmark/benchmark.rs 2018-08-11 15:57:00.000000000 +0000 +++ rust-ryu-1.0.0/benchmark/benchmark.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -extern crate rand; -extern crate ryu; - -use rand::{Rng, SeedableRng}; - -const SAMPLES: usize = 10000; -const ITERATIONS: usize = 1000; - -struct MeanAndVariance { - n: i64, - mean: f64, - m2: f64, -} - -impl MeanAndVariance { - fn new() -> Self { - MeanAndVariance { - n: 0, - mean: 0.0, - m2: 0.0, - } - } - - fn update(&mut self, x: f64) { - self.n += 1; - let d = x - self.mean; - self.mean += d / self.n as f64; - let d2 = x - self.mean; - self.m2 += d * d2; - } - - fn variance(&self) -> f64 { - self.m2 / (self.n - 1) as f64 - } - - fn stddev(&self) -> f64 { - self.variance().sqrt() - } -} - -macro_rules! benchmark { - ($name:ident, $ty:ident) => { - fn $name() -> usize { - let mut rng = rand::prng::XorShiftRng::from_seed([123u8; 16]); - let mut mv = MeanAndVariance::new(); - let mut throwaway = 0; - for _ in 0..SAMPLES { - let f = loop { - let f = $ty::from_bits(rng.gen()); - if f.is_finite() { - break f; - } - }; - - let t1 = std::time::SystemTime::now(); - for _ in 0..ITERATIONS { - throwaway += ryu::Buffer::new().format(f).len(); - } - let duration = t1.elapsed().unwrap(); - let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; - mv.update(nanos as f64 / ITERATIONS as f64); - } - println!( - "{:12} {:8.3} {:8.3}", - concat!(stringify!($name), ":"), - mv.mean, - mv.stddev(), - ); - throwaway - } - }; -} - -benchmark!(pretty32, f32); -benchmark!(pretty64, f64); - -fn main() { - println!("{:>20}{:>9}", "Average", "Stddev"); - let mut throwaway = 0; - throwaway += pretty32(); - throwaway += pretty64(); - if std::env::var_os("ryu-benchmark").is_some() { - // Prevent the compiler from optimizing the code away. - println!("{}", throwaway); - } -} diff -Nru rust-ryu-0.2.7/Cargo.toml rust-ryu-1.0.0/Cargo.toml --- rust-ryu-0.2.7/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-ryu-1.0.0/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -12,7 +12,7 @@ [package] name = "ryu" -version = "0.2.7" +version = "1.0.0" authors = ["David Tolnay "] build = "build.rs" description = "Fast floating point to string conversion" @@ -20,10 +20,6 @@ readme = "README.md" license = "Apache-2.0 OR BSL-1.0" repository = "https://github.com/dtolnay/ryu" - -[[example]] -name = "benchmark" -path = "benchmark/benchmark.rs" [dependencies.no-panic] version = "0.1" optional = true diff -Nru rust-ryu-0.2.7/Cargo.toml.orig rust-ryu-1.0.0/Cargo.toml.orig --- rust-ryu-0.2.7/Cargo.toml.orig 2018-11-10 22:33:44.000000000 +0000 +++ rust-ryu-1.0.0/Cargo.toml.orig 2019-06-11 21:14:25.000000000 +0000 @@ -1,6 +1,6 @@ [package] name = "ryu" -version = "0.2.7" +version = "1.0.0" authors = ["David Tolnay "] license = "Apache-2.0 OR BSL-1.0" description = "Fast floating point to string conversion" @@ -16,10 +16,6 @@ # one case, and only f64) at the cost of some performance. small = [] -[[example]] -name = "benchmark" -path = "benchmark/benchmark.rs" - [dependencies] no-panic = { version = "0.1", optional = true } diff -Nru rust-ryu-0.2.7/.cargo_vcs_info.json rust-ryu-1.0.0/.cargo_vcs_info.json --- rust-ryu-0.2.7/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-ryu-1.0.0/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "5edafe24373c9884609fc2e04c0eba0770df47a8" + "sha1": "a0e85ea2eafbc23989ecfac1139b098d26155aee" } } diff -Nru rust-ryu-0.2.7/debian/cargo-checksum.json rust-ryu-1.0.0/debian/cargo-checksum.json --- rust-ryu-0.2.7/debian/cargo-checksum.json 2018-12-02 10:54:36.000000000 +0000 +++ rust-ryu-1.0.0/debian/cargo-checksum.json 2019-08-09 17:12:28.000000000 +0000 @@ -1 +1 @@ -{"package":"eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7","files":{}} +{"package":"c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997","files":{}} diff -Nru rust-ryu-0.2.7/debian/changelog rust-ryu-1.0.0/debian/changelog --- rust-ryu-0.2.7/debian/changelog 2018-12-02 10:54:36.000000000 +0000 +++ rust-ryu-1.0.0/debian/changelog 2019-08-09 17:12:28.000000000 +0000 @@ -1,3 +1,9 @@ +rust-ryu (1.0.0-1) unstable; urgency=medium + + * Package ryu 1.0.0 from crates.io using debcargo 2.4.0 + + -- Wolfgang Silbermayr Fri, 09 Aug 2019 19:12:28 +0200 + rust-ryu (0.2.7-1) unstable; urgency=medium * Package ryu 0.2.7 from crates.io using debcargo 2.2.9 diff -Nru rust-ryu-0.2.7/debian/control rust-ryu-1.0.0/debian/control --- rust-ryu-0.2.7/debian/control 2018-12-02 10:54:36.000000000 +0000 +++ rust-ryu-1.0.0/debian/control 2019-08-09 17:12:28.000000000 +0000 @@ -2,7 +2,7 @@ Section: rust Priority: optional Build-Depends: debhelper (>= 11), - dh-cargo (>= 10), + dh-cargo (>= 18), cargo:native , rustc:native , libstd-rust-dev @@ -23,15 +23,15 @@ Provides: librust-ryu+default-dev (= ${binary:Version}), librust-ryu+small-dev (= ${binary:Version}), - librust-ryu-0-dev (= ${binary:Version}), - librust-ryu-0+default-dev (= ${binary:Version}), - librust-ryu-0+small-dev (= ${binary:Version}), - librust-ryu-0.2-dev (= ${binary:Version}), - librust-ryu-0.2+default-dev (= ${binary:Version}), - librust-ryu-0.2+small-dev (= ${binary:Version}), - librust-ryu-0.2.7-dev (= ${binary:Version}), - librust-ryu-0.2.7+default-dev (= ${binary:Version}), - librust-ryu-0.2.7+small-dev (= ${binary:Version}) + librust-ryu-1-dev (= ${binary:Version}), + librust-ryu-1+default-dev (= ${binary:Version}), + librust-ryu-1+small-dev (= ${binary:Version}), + librust-ryu-1.0-dev (= ${binary:Version}), + librust-ryu-1.0+default-dev (= ${binary:Version}), + librust-ryu-1.0+small-dev (= ${binary:Version}), + librust-ryu-1.0.0-dev (= ${binary:Version}), + librust-ryu-1.0.0+default-dev (= ${binary:Version}), + librust-ryu-1.0.0+small-dev (= ${binary:Version}) Description: Fast floating point to string conversion - Rust source code This package contains the source for the Rust ryu crate, packaged by debcargo for use with cargo and dh-cargo. @@ -44,9 +44,9 @@ librust-ryu-dev (= ${binary:Version}), librust-no-panic-0.1+default-dev Provides: - librust-ryu-0+no-panic-dev (= ${binary:Version}), - librust-ryu-0.2+no-panic-dev (= ${binary:Version}), - librust-ryu-0.2.7+no-panic-dev (= ${binary:Version}) + librust-ryu-1+no-panic-dev (= ${binary:Version}), + librust-ryu-1.0+no-panic-dev (= ${binary:Version}), + librust-ryu-1.0.0+no-panic-dev (= ${binary:Version}) Description: Fast floating point to string conversion - feature "no-panic" - This metapackage enables feature no-panic for the Rust ryu crate, by pulling in - any additional dependencies needed by that feature. + This metapackage enables feature "no-panic" for the Rust ryu crate, by pulling + in any additional dependencies needed by that feature. diff -Nru rust-ryu-0.2.7/debian/copyright.debcargo.hint rust-ryu-1.0.0/debian/copyright.debcargo.hint --- rust-ryu-0.2.7/debian/copyright.debcargo.hint 2018-12-02 10:54:36.000000000 +0000 +++ rust-ryu-1.0.0/debian/copyright.debcargo.hint 2019-08-09 17:12:28.000000000 +0000 @@ -84,8 +84,8 @@ Files: debian/* Copyright: - 2018 Debian Rust Maintainers - 2018 Wolfgang Silbermayr + 2018-2019 Debian Rust Maintainers + 2018-2019 Wolfgang Silbermayr License: Apache-2.0 or BSL-1.0 License: Apache-2.0 diff -Nru rust-ryu-0.2.7/debian/tests/control rust-ryu-1.0.0/debian/tests/control --- rust-ryu-0.2.7/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rust-ryu-1.0.0/debian/tests/control 2019-08-09 17:12:28.000000000 +0000 @@ -0,0 +1,11 @@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test ryu 1.0.0 --all-targets --all-features +Depends: dh-cargo (>= 18), librust-num-cpus-1+default-dev (>= 1.8-~~), librust-rand-0.5+default-dev, @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test ryu 1.0.0 --all-targets --no-default-features +Depends: dh-cargo (>= 18), librust-num-cpus-1+default-dev (>= 1.8-~~), librust-rand-0.5+default-dev, librust-ryu-dev +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test ryu 1.0.0 --all-targets --features no-panic +Depends: dh-cargo (>= 18), librust-num-cpus-1+default-dev (>= 1.8-~~), librust-rand-0.5+default-dev, librust-ryu+no-panic-dev +Restrictions: allow-stderr, skip-not-installable diff -Nru rust-ryu-0.2.7/examples/upstream_benchmark.rs rust-ryu-1.0.0/examples/upstream_benchmark.rs --- rust-ryu-0.2.7/examples/upstream_benchmark.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-ryu-1.0.0/examples/upstream_benchmark.rs 2019-06-11 21:13:49.000000000 +0000 @@ -0,0 +1,88 @@ +// cargo run --example upstream_benchmark --release + +extern crate rand; +extern crate ryu; + +use rand::{Rng, SeedableRng}; + +const SAMPLES: usize = 10000; +const ITERATIONS: usize = 1000; + +struct MeanAndVariance { + n: i64, + mean: f64, + m2: f64, +} + +impl MeanAndVariance { + fn new() -> Self { + MeanAndVariance { + n: 0, + mean: 0.0, + m2: 0.0, + } + } + + fn update(&mut self, x: f64) { + self.n += 1; + let d = x - self.mean; + self.mean += d / self.n as f64; + let d2 = x - self.mean; + self.m2 += d * d2; + } + + fn variance(&self) -> f64 { + self.m2 / (self.n - 1) as f64 + } + + fn stddev(&self) -> f64 { + self.variance().sqrt() + } +} + +macro_rules! benchmark { + ($name:ident, $ty:ident) => { + fn $name() -> usize { + let mut rng = rand::prng::XorShiftRng::from_seed([123u8; 16]); + let mut mv = MeanAndVariance::new(); + let mut throwaway = 0; + for _ in 0..SAMPLES { + let f = loop { + let f = $ty::from_bits(rng.gen()); + if f.is_finite() { + break f; + } + }; + + let t1 = std::time::SystemTime::now(); + for _ in 0..ITERATIONS { + throwaway += ryu::Buffer::new().format_finite(f).len(); + } + let duration = t1.elapsed().unwrap(); + let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; + mv.update(nanos as f64 / ITERATIONS as f64); + } + println!( + "{:12} {:8.3} {:8.3}", + concat!(stringify!($name), ":"), + mv.mean, + mv.stddev(), + ); + throwaway + } + }; +} + +benchmark!(pretty32, f32); +benchmark!(pretty64, f64); + +fn main() { + println!("{:>20}{:>9}", "Average", "Stddev"); + let mut throwaway = 0; + throwaway += pretty32(); + throwaway += pretty64(); + if std::env::var_os("ryu-benchmark").is_some() { + // Prevent the compiler from optimizing the code away. + println!("{}", throwaway); + } +} diff -Nru rust-ryu-0.2.7/README.md rust-ryu-1.0.0/README.md --- rust-ryu-0.2.7/README.md 2018-10-07 00:33:23.000000000 +0000 +++ rust-ryu-1.0.0/README.md 2019-06-11 21:14:38.000000000 +0000 @@ -13,29 +13,22 @@ under the creative commons CC-BY-SA license. This Rust implementation is a line-by-line port of Ulf Adams' implementation in -C, [https://github.com/ulfjack/ryu][upstream]. The `ryu::raw` module exposes -exactly the API and formatting of the C implementation as unsafe pure Rust -functions. There is additionally a safe API as demonstrated in the example code -below. The safe API uses the same underlying Ryū algorithm but diverges from the -formatting of the C implementation to produce more human-readable output, for -example `0.3` rather than `3E-1`. +C, [https://github.com/ulfjack/ryu][upstream]. *Requirements: this crate supports any compiler version back to rustc 1.15; it uses nothing from the Rust standard library so is usable from no_std crates.* [paper]: https://dl.acm.org/citation.cfm?id=3192369 -[upstream]: https://github.com/ulfjack/ryu/tree/66ba13274ca0247ad46fd4fe8a2f6d36f5d39b01 +[upstream]: https://github.com/ulfjack/ryu/tree/688f43b62276b400728baad54afc32c3ab9c1a95 ```toml [dependencies] -ryu = "0.2" +ryu = "1.0" ``` -## Examples +## Example ```rust -extern crate ryu; - fn main() { let mut buffer = ryu::Buffer::new(); let printed = buffer.format(1.234); @@ -53,12 +46,12 @@ $ bazel run -c opt //ryu/benchmark ``` -And our benchmarks with: +And the same benchmark against our implementation with: ```console -$ git clone https://github.com/ulfjack/ryu rust-ryu +$ git clone https://github.com/dtolnay/ryu rust-ryu $ cd rust-ryu -$ cargo run --example benchmark --release +$ cargo run --example upstream_benchmark --release ``` These benchmarks measure the average time to print a 32-bit float and average @@ -69,9 +62,53 @@ all perform the same, taking around 21 nanoseconds to format a 32-bit float and 31 nanoseconds to format a 64-bit float. -## License +There is also a Rust-specific benchmark comparing this implementation to the +standard library which you can run with: + +```console +$ cargo bench +``` + +The benchmark shows Ryu approximately 4-10x faster than the standard library +across a range of f32 and f64 inputs. Measurements are in nanoseconds per +iteration; smaller is better. + +| type=f32 | 0.0 | 0.1234 | 2.718281828459045 | f32::MAX | +|:--------:|:----:|:------:|:-----------------:|:--------:| +| RYU | 3ns | 28ns | 23ns | 22ns | +| STD | 40ns | 106ns | 128ns | 110ns | + +| type=f64 | 0.0 | 0.1234 | 2.718281828459045 | f64::MAX | +|:--------:|:----:|:------:|:-----------------:|:--------:| +| RYU | 3ns | 50ns | 35ns | 32ns | +| STD | 39ns | 105ns | 128ns | 202ns | + +## Formatting + +This library tends to produce more human-readable output than the standard +library's to\_string, which never uses scientific notation. Here are two +examples: + +- *ryu:* 1.23e40, *std:* 12300000000000000000000000000000000000000 +- *ryu:* 1.23e-40, *std:* 0.000000000000000000000000000000000000000123 + +Both libraries print short decimals such as 0.0000123 without scientific +notation. + +
+ +#### License + + +Licensed under either of Apache License, Version +2.0 or Boost Software License 1.0 at your +option. + -Licensed under either of the following at your option. +
-- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- Boost Software License 1.0 ([LICENSE-BOOST](LICENSE-BOOST) or https://www.boost.org/LICENSE_1_0.txt) + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + diff -Nru rust-ryu-0.2.7/src/buffer/mod.rs rust-ryu-1.0.0/src/buffer/mod.rs --- rust-ryu-0.2.7/src/buffer/mod.rs 2018-08-08 06:34:49.000000000 +0000 +++ rust-ryu-1.0.0/src/buffer/mod.rs 2019-06-11 21:13:49.000000000 +0000 @@ -1,17 +1,21 @@ use core::{mem, slice, str}; -use pretty; +use raw; #[cfg(feature = "no-panic")] use no_panic::no_panic; +const NAN: &'static str = "NaN"; +const INFINITY: &'static str = "inf"; +const NEG_INFINITY: &'static str = "-inf"; + /// Safe API for formatting floating point numbers to text. /// /// ## Example /// -/// ```rust +/// ```edition2018 /// let mut buffer = ryu::Buffer::new(); -/// let printed = buffer.format(1.234); +/// let printed = buffer.format_finite(1.234); /// assert_eq!(printed, "1.234"); /// ``` #[derive(Copy, Clone)] @@ -35,6 +39,27 @@ /// /// # Special cases /// + /// This function formats NaN as the string "NaN", positive infinity as + /// "inf", and negative infinity as "-inf" to match std::fmt. + /// + /// If your input is known to be finite, you may get better performance by + /// calling the `format_finite` method instead of `format` to avoid the + /// checks for special cases. + #[cfg_attr(feature = "no-panic", inline)] + #[cfg_attr(feature = "no-panic", no_panic)] + pub fn format(&mut self, f: F) -> &str { + if f.is_nonfinite() { + f.format_nonfinite() + } else { + self.format_finite(f) + } + } + + /// Print a floating point number into this buffer and return a reference to + /// its string representation within the buffer. + /// + /// # Special cases + /// /// This function **does not** check for NaN or infinity. If the input /// number is not a finite float, the printed representation will be some /// correctly formatted but unspecified numerical value. @@ -47,7 +72,7 @@ /// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite #[inline] #[cfg_attr(feature = "no-panic", no_panic)] - pub fn format(&mut self, f: F) -> &str { + pub fn format_finite(&mut self, f: F) -> &str { unsafe { let n = f.write_to_ryu_buffer(&mut self.bytes[0]); debug_assert!(n <= self.bytes.len()); @@ -70,28 +95,70 @@ /// /// This trait is sealed and cannot be implemented for types outside of the /// `ryu` crate. -pub trait Float: Sealed { - // Not public API. - #[doc(hidden)] +pub trait Float: Sealed {} +impl Float for f32 {} +impl Float for f64 {} + +pub trait Sealed: Copy { + fn is_nonfinite(self) -> bool; + fn format_nonfinite(self) -> &'static str; unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize; } -impl Float for f32 { +impl Sealed for f32 { + #[inline] + fn is_nonfinite(self) -> bool { + const EXP_MASK: u32 = 0x7f800000; + let bits = unsafe { mem::transmute::(self) }; + bits & EXP_MASK == EXP_MASK + } + + #[cold] + #[cfg_attr(feature = "no-panic", inline)] + fn format_nonfinite(self) -> &'static str { + const MANTISSA_MASK: u32 = 0x007fffff; + const SIGN_MASK: u32 = 0x80000000; + let bits = unsafe { mem::transmute::(self) }; + if bits & MANTISSA_MASK != 0 { + NAN + } else if bits & SIGN_MASK != 0 { + NEG_INFINITY + } else { + INFINITY + } + } + #[inline] - #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { - pretty::f2s_buffered_n(self, result) + raw::format32(self, result) } } -impl Float for f64 { +impl Sealed for f64 { + #[inline] + fn is_nonfinite(self) -> bool { + const EXP_MASK: u64 = 0x7ff0000000000000; + let bits = unsafe { mem::transmute::(self) }; + bits & EXP_MASK == EXP_MASK + } + + #[cold] + #[cfg_attr(feature = "no-panic", inline)] + fn format_nonfinite(self) -> &'static str { + const MANTISSA_MASK: u64 = 0x000fffffffffffff; + const SIGN_MASK: u64 = 0x8000000000000000; + let bits = unsafe { mem::transmute::(self) }; + if bits & MANTISSA_MASK != 0 { + NAN + } else if bits & SIGN_MASK != 0 { + NEG_INFINITY + } else { + INFINITY + } + } + #[inline] - #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { - pretty::d2s_buffered_n(self, result) + raw::format64(self, result) } } - -pub trait Sealed {} -impl Sealed for f32 {} -impl Sealed for f64 {} diff -Nru rust-ryu-0.2.7/src/common.rs rust-ryu-1.0.0/src/common.rs --- rust-ryu-0.2.7/src/common.rs 2018-08-07 18:42:40.000000000 +0000 +++ rust-ryu-1.0.0/src/common.rs 2019-06-11 21:13:49.000000000 +0000 @@ -18,55 +18,58 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. -use core::ptr; +#[cfg_attr(feature = "no-panic", inline)] +pub fn decimal_length9(v: u32) -> u32 { + // Function precondition: v is not a 10-digit number. + // (f2s: 9 digits are sufficient for round-tripping.) + debug_assert!(v < 1000000000); + + if v >= 100000000 { + 9 + } else if v >= 10000000 { + 8 + } else if v >= 1000000 { + 7 + } else if v >= 100000 { + 6 + } else if v >= 10000 { + 5 + } else if v >= 1000 { + 4 + } else if v >= 100 { + 3 + } else if v >= 10 { + 2 + } else { + 1 + } +} // Returns e == 0 ? 1 : ceil(log_2(5^e)). #[cfg_attr(feature = "no-panic", inline)] -pub fn pow5bits(e: i32) -> u32 { +pub fn pow5bits(e: i32) -> i32 { // This approximation works up to the point that the multiplication overflows at e = 3529. // If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater // than 2^9297. debug_assert!(e >= 0); debug_assert!(e <= 3528); - ((e as u32 * 1217359) >> 19) + 1 + (((e as u32 * 1217359) >> 19) + 1) as i32 } // Returns floor(log_10(2^e)). #[cfg_attr(feature = "no-panic", inline)] -pub fn log10_pow2(e: i32) -> i32 { +pub fn log10_pow2(e: i32) -> u32 { // The first value this approximation fails for is 2^1651 which is just greater than 10^297. debug_assert!(e >= 0); debug_assert!(e <= 1650); - ((e as u32 * 78913) >> 18) as i32 + (e as u32 * 78913) >> 18 } // Returns floor(log_10(5^e)). #[cfg_attr(feature = "no-panic", inline)] -pub fn log10_pow5(e: i32) -> i32 { +pub fn log10_pow5(e: i32) -> u32 { // The first value this approximation fails for is 5^2621 which is just greater than 10^1832. debug_assert!(e >= 0); debug_assert!(e <= 2620); - ((e as u32 * 732923) >> 20) as i32 -} - -#[cfg_attr(feature = "no-panic", inline)] -pub unsafe fn copy_special_str( - result: *mut u8, - sign: bool, - exponent: bool, - mantissa: bool, -) -> usize { - if mantissa { - ptr::copy_nonoverlapping(b"NaN".as_ptr(), result, 3); - return 3; - } - if sign { - *result = b'-'; - } - if exponent { - ptr::copy_nonoverlapping(b"Infinity".as_ptr(), result.offset(sign as isize), 8); - return sign as usize + 8; - } - ptr::copy_nonoverlapping(b"0E0".as_ptr(), result.offset(sign as isize), 3); - sign as usize + 3 + (e as u32 * 732923) >> 20 } diff -Nru rust-ryu-0.2.7/src/d2s_intrinsics.rs rust-ryu-1.0.0/src/d2s_intrinsics.rs --- rust-ryu-0.2.7/src/d2s_intrinsics.rs 2018-08-20 06:48:34.000000000 +0000 +++ rust-ryu-1.0.0/src/d2s_intrinsics.rs 2019-06-11 21:13:49.000000000 +0000 @@ -44,7 +44,7 @@ let mid2_hi = (mid2 >> 32) as u32; let p_hi = b11 + mid1_hi as u64 + mid2_hi as u64; - let p_lo = ((mid2_lo as u64) << 32) + b00_lo as u64; + let p_lo = ((mid2_lo as u64) << 32) | b00_lo as u64; (p_lo, p_hi) } @@ -74,6 +74,32 @@ } #[cfg_attr(feature = "no-panic", inline)] -pub fn div100_000_000(x: u64) -> u64 { - x / 100_000_000 +fn pow5_factor(mut value: u64) -> u32 { + let mut count = 0u32; + loop { + debug_assert!(value != 0); + let q = div5(value); + let r = (value as u32).wrapping_sub(5u32.wrapping_mul(q as u32)); + if r != 0 { + break; + } + value = q; + count += 1; + } + count +} + +// Returns true if value is divisible by 5^p. +#[cfg_attr(feature = "no-panic", inline)] +pub fn multiple_of_power_of_5(value: u64, p: u32) -> bool { + // I tried a case distinction on p, but there was no performance difference. + pow5_factor(value) >= p +} + +// Returns true if value is divisible by 2^p. +#[cfg_attr(feature = "no-panic", inline)] +pub fn multiple_of_power_of_2(value: u64, p: u32) -> bool { + debug_assert!(value != 0); + // return __builtin_ctzll(value) >= p; + (value & ((1u64 << p) - 1)) == 0 } diff -Nru rust-ryu-0.2.7/src/d2s.rs rust-ryu-1.0.0/src/d2s.rs --- rust-ryu-0.2.7/src/d2s.rs 2018-11-10 21:59:02.000000000 +0000 +++ rust-ryu-1.0.0/src/d2s.rs 2019-06-11 21:13:49.000000000 +0000 @@ -18,55 +18,22 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. -use core::{mem, ptr}; +use core::mem; use common::*; #[cfg(not(feature = "small"))] use d2s_full_table::*; +use d2s_intrinsics::*; #[cfg(feature = "small")] use d2s_small_table::*; -use digit_table::*; -use d2s_intrinsics::*; - -#[cfg(feature = "no-panic")] -use no_panic::no_panic; pub const DOUBLE_MANTISSA_BITS: u32 = 52; pub const DOUBLE_EXPONENT_BITS: u32 = 11; +const DOUBLE_BIAS: i32 = 1023; const DOUBLE_POW5_INV_BITCOUNT: i32 = 122; const DOUBLE_POW5_BITCOUNT: i32 = 121; -#[cfg_attr(feature = "no-panic", inline)] -fn pow5_factor(mut value: u64) -> u32 { - let mut count = 0u32; - loop { - debug_assert!(value != 0); - let q = div5(value); - let r = (value - 5 * q) as u32; - if r != 0 { - break; - } - value = q; - count += 1; - } - count -} - -// Returns true if value is divisible by 5^p. -#[cfg_attr(feature = "no-panic", inline)] -fn multiple_of_power_of_5(value: u64, p: u32) -> bool { - // I tried a case distinction on p, but there was no performance difference. - pow5_factor(value) >= p -} - -// Returns true if value is divisible by 2^p. -#[cfg_attr(feature = "no-panic", inline)] -fn multiple_of_power_of_2(value: u64, p: u32) -> bool { - // return __builtin_ctzll(value) >= p; - (value & ((1u64 << p) - 1)) == 0 -} - #[cfg(integer128)] #[cfg_attr(feature = "no-panic", inline)] fn mul_shift(m: u64, mul: &(u64, u64), j: u32) -> u64 { @@ -131,7 +98,7 @@ } #[cfg_attr(feature = "no-panic", inline)] -pub fn decimal_length(v: u64) -> u32 { +pub fn decimal_length17(v: u64) -> u32 { // This is slightly faster than a loop. // The average output length is 16.38 digits, so we check high-to-low. // Function precondition: v is not an 18, 19, or 20-digit number. @@ -178,29 +145,29 @@ // A floating decimal representing m * 10^e. pub struct FloatingDecimal64 { pub mantissa: u64, + // Decimal exponent's range is -324 to 308 + // inclusive, and can fit in i16 if needed. pub exponent: i32, } #[cfg_attr(feature = "no-panic", inline)] pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 { - let bias = (1u32 << (DOUBLE_EXPONENT_BITS - 1)) - 1; - let (e2, m2) = if ieee_exponent == 0 { ( // We subtract 2 so that the bounds computation has 2 additional bits. - 1 - bias as i32 - DOUBLE_MANTISSA_BITS as i32 - 2, + 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2, ieee_mantissa, ) } else { ( - ieee_exponent as i32 - bias as i32 - DOUBLE_MANTISSA_BITS as i32 - 2, + ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2, (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa, ) }; let even = (m2 & 1) == 0; let accept_bounds = even; - // Step 2: Determine the interval of legal decimal representations. + // Step 2: Determine the interval of valid decimal representations. let mv = 4 * m2; // Implicit bool -> int conversion. True is 1, false is 0. let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32; @@ -218,9 +185,9 @@ if e2 >= 0 { // I tried special-casing q == 0, but there was no effect on performance. // This expression is slightly faster than max(0, log10_pow2(e2) - 1). - let q = (log10_pow2(e2) - (e2 > 3) as i32) as u32; + let q = log10_pow2(e2) - (e2 > 3) as u32; e10 = q as i32; - let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) as i32 - 1; + let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1; let i = -e2 + q as i32 + k; vr = mul_shift_all( m2, @@ -242,7 +209,7 @@ // This should use q <= 22, but I think 21 is also safe. Smaller values // may still be safe, but it's more difficult to reason about them. // Only one of mp, mv, and mm can be a multiple of 5, if any. - let mv_mod5 = (mv - 5 * div5(mv)) as u32; + let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32)); if mv_mod5 == 0 { vr_is_trailing_zeros = multiple_of_power_of_5(mv, q); } else if accept_bounds { @@ -257,10 +224,10 @@ } } else { // This expression is slightly faster than max(0, log10_pow5(-e2) - 1). - let q = (log10_pow5(-e2) - (-e2 > 1) as i32) as u32; + let q = log10_pow5(-e2) - (-e2 > 1) as u32; e10 = q as i32 + e2; let i = -e2 - q as i32; - let k = pow5bits(i) as i32 - DOUBLE_POW5_BITCOUNT; + let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT; let j = q as i32 - k; vr = mul_shift_all( m2, @@ -291,17 +258,16 @@ } } else if q < 63 { // TODO(ulfjack): Use a tighter bound here. - // We need to compute min(ntz(mv), pow5_factor(mv) - e2) >= q - 1 - // <=> ntz(mv) >= q - 1 && pow5_factor(mv) - e2 >= q - 1 - // <=> ntz(mv) >= q - 1 (e2 is negative and -e2 >= q) - // <=> (mv & ((1 << (q - 1)) - 1)) == 0 - // We also need to make sure that the left shift does not overflow. - vr_is_trailing_zeros = multiple_of_power_of_2(mv, q - 1); + // We want to know if the full product has at least q trailing zeros. + // We need to compute min(p2(mv), p5(mv) - e2) >= q + // <=> p2(mv) >= q && p5(mv) - e2 >= q + // <=> p2(mv) >= q (because -e2 >= q) + vr_is_trailing_zeros = multiple_of_power_of_2(mv, q); } } - // Step 4: Find the shortest decimal representation in the interval of legal representations. - let mut removed = 0u32; + // Step 4: Find the shortest decimal representation in the interval of valid representations. + let mut removed = 0i32; let mut last_removed_digit = 0u8; // On average, we remove ~2 digits. let output = if vm_is_trailing_zeros || vr_is_trailing_zeros { @@ -312,9 +278,9 @@ if vp_div10 <= vm_div10 { break; } - let vm_mod10 = (vm - 10 * vm_div10) as u32; + let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32)); let vr_div10 = div10(vr); - let vr_mod10 = (vr - 10 * vr_div10) as u32; + let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32)); vm_is_trailing_zeros &= vm_mod10 == 0; vr_is_trailing_zeros &= last_removed_digit == 0; last_removed_digit = vr_mod10 as u8; @@ -326,13 +292,13 @@ if vm_is_trailing_zeros { loop { let vm_div10 = div10(vm); - let vm_mod10 = (vm - 10 * vm_div10) as u32; + let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32)); if vm_mod10 != 0 { break; } let vp_div10 = div10(vp); let vr_div10 = div10(vr); - let vr_mod10 = (vr - 10 * vr_div10) as u32; + let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32)); vr_is_trailing_zeros &= last_removed_digit == 0; last_removed_digit = vr_mod10 as u8; vr = vr_div10; @@ -356,7 +322,7 @@ // Optimization: remove two digits at a time (~86.2%). if vp_div100 > vm_div100 { let vr_div100 = div100(vr); - let vr_mod100 = (vr - 100 * vr_div100) as u32; + let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32)); round_up = vr_mod100 >= 50; vr = vr_div100; vp = vp_div100; @@ -374,7 +340,7 @@ break; } let vr_div10 = div10(vr); - let vr_mod10 = (vr - 10 * vr_div10) as u32; + let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32)); round_up = vr_mod10 >= 5; vr = vr_div10; vp = vp_div10; @@ -384,199 +350,10 @@ // We need to take vr + 1 if vr is outside bounds or we need to round up. vr + (vr == vm || round_up) as u64 }; - let exp = e10 + removed as i32; + let exp = e10 + removed; FloatingDecimal64 { exponent: exp, mantissa: output, } } - -#[cfg_attr(feature = "no-panic", inline)] -unsafe fn to_chars(v: FloatingDecimal64, sign: bool, result: *mut u8) -> usize { - // Step 5: Print the decimal representation. - let mut index = 0isize; - if sign { - *result.offset(index) = b'-'; - index += 1; - } - - let mut output = v.mantissa; - let olength = decimal_length(output); - - // Print the decimal digits. - // The following code is equivalent to: - // for (uint32_t i = 0; i < olength - 1; ++i) { - // const uint32_t c = output % 10; output /= 10; - // result[index + olength - i] = (char) ('0' + c); - // } - // result[index] = '0' + output % 10; - - let mut i = 0isize; - // We prefer 32-bit operations, even on 64-bit platforms. - // We have at most 17 digits, and uint32_t can store 9 digits. - // If output doesn't fit into uint32_t, we cut off 8 digits, - // so the rest will fit into uint32_t. - if (output >> 32) != 0 { - // Expensive 64-bit division. - let q = div100_000_000(output); - let mut output2 = (output - 100_000_000 * q) as u32; - output = q; - - let c = output2 % 10000; - output2 /= 10000; - let d = output2 % 10000; - let c0 = (c % 100) << 1; - let c1 = (c / 100) << 1; - let d0 = (d % 100) << 1; - let d1 = (d / 100) << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c0 as usize), - result.offset(index + olength as isize - i - 1), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c1 as usize), - result.offset(index + olength as isize - i - 3), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(d0 as usize), - result.offset(index + olength as isize - i - 5), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(d1 as usize), - result.offset(index + olength as isize - i - 7), - 2, - ); - i += 8; - } - let mut output2 = output as u32; - while output2 >= 10000 { - let c = (output2 - 10000 * (output2 / 10000)) as u32; - output2 /= 10000; - let c0 = (c % 100) << 1; - let c1 = (c / 100) << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c0 as usize), - result.offset(index + olength as isize - i - 1), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c1 as usize), - result.offset(index + olength as isize - i - 3), - 2, - ); - i += 4; - } - if output2 >= 100 { - let c = ((output2 % 100) << 1) as u32; - output2 /= 100; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c as usize), - result.offset(index + olength as isize - i - 1), - 2, - ); - i += 2; - } - if output2 >= 10 { - let c = (output2 << 1) as u32; - // We can't use memcpy here: the decimal dot goes between these two digits. - *result.offset(index + olength as isize - i) = *DIGIT_TABLE.get_unchecked(c as usize + 1); - *result.offset(index) = *DIGIT_TABLE.get_unchecked(c as usize); - } else { - *result.offset(index) = b'0' + output2 as u8; - } - - // Print decimal point if needed. - if olength > 1 { - *result.offset(index + 1) = b'.'; - index += olength as isize + 1; - } else { - index += 1; - } - - // Print the exponent. - *result.offset(index) = b'E'; - index += 1; - let mut exp = v.exponent as i32 + olength as i32 - 1; - if exp < 0 { - *result.offset(index) = b'-'; - index += 1; - exp = -exp; - } - - if exp >= 100 { - let c = exp % 10; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked((2 * (exp / 10)) as usize), - result.offset(index), - 2, - ); - *result.offset(index + 2) = b'0' + c as u8; - index += 3; - } else if exp >= 10 { - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked((2 * exp) as usize), - result.offset(index), - 2, - ); - index += 2; - } else { - *result.offset(index) = b'0' + exp as u8; - index += 1; - } - - debug_assert!(index <= 24); - index as usize -} - -/// Print f64 to the given buffer and return number of bytes written. Ryū's -/// original formatting. -/// -/// At most 24 bytes will be written. -/// -/// ## Special cases -/// -/// This function represents any NaN as `NaN`, positive infinity as `Infinity`, -/// and negative infinity as `-Infinity`. -/// -/// ## Safety -/// -/// The `result` pointer argument must point to sufficiently many writable bytes -/// to hold Ryū's representation of `f`. -/// -/// ## Example -/// -/// ```rust -/// let f = 1.234f64; -/// -/// unsafe { -/// let mut buffer: [u8; 24] = std::mem::uninitialized(); -/// let n = ryu::raw::d2s_buffered_n(f, &mut buffer[0]); -/// let s = std::str::from_utf8_unchecked(&buffer[..n]); -/// assert_eq!(s, "1.234E0"); -/// } -/// ``` -#[cfg_attr(must_use_return, must_use)] -#[cfg_attr(feature = "no-panic", no_panic)] -pub unsafe fn d2s_buffered_n(f: f64, result: *mut u8) -> usize { - // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. - let bits = mem::transmute::(f); - - // Decode bits into sign, mantissa, and exponent. - let ieee_sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; - let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1); - let ieee_exponent = - (bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1); - // Case distinction; exit early for the easy cases. - if ieee_exponent == ((1u32 << DOUBLE_EXPONENT_BITS) - 1) - || (ieee_exponent == 0 && ieee_mantissa == 0) - { - return copy_special_str(result, ieee_sign, ieee_exponent != 0, ieee_mantissa != 0); - } - - let v = d2d(ieee_mantissa, ieee_exponent); - to_chars(v, ieee_sign, result) -} diff -Nru rust-ryu-0.2.7/src/d2s_small_table.rs rust-ryu-1.0.0/src/d2s_small_table.rs --- rust-ryu-0.2.7/src/d2s_small_table.rs 2018-08-20 06:48:34.000000000 +0000 +++ rust-ryu-1.0.0/src/d2s_small_table.rs 2019-04-25 02:29:05.000000000 +0000 @@ -168,9 +168,9 @@ let delta = pow5bits(i as i32) - pow5bits(base2 as i32); debug_assert!(base < POW5_OFFSETS.len() as u32); ( - shiftright128(low0, sum, delta) + shiftright128(low0, sum, delta as u32) + ((*POW5_OFFSETS.get_unchecked(base as usize) >> offset) & 1) as u64, - shiftright128(sum, high1, delta), + shiftright128(sum, high1, delta as u32), ) } @@ -198,9 +198,9 @@ let delta = pow5bits(base2 as i32) - pow5bits(i as i32); debug_assert!(base < POW5_INV_OFFSETS.len() as u32); ( - shiftright128(low0, sum, delta) + shiftright128(low0, sum, delta as u32) + 1 + ((*POW5_INV_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u64, - shiftright128(sum, high1, delta), + shiftright128(sum, high1, delta as u32), ) } diff -Nru rust-ryu-0.2.7/src/f2s.rs rust-ryu-1.0.0/src/f2s.rs --- rust-ryu-0.2.7/src/f2s.rs 2018-11-10 21:59:45.000000000 +0000 +++ rust-ryu-1.0.0/src/f2s.rs 2019-06-11 21:13:49.000000000 +0000 @@ -18,17 +18,12 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. -use core::{mem, ptr}; - use common::*; -use digit_table::*; - -#[cfg(feature = "no-panic")] -use no_panic::no_panic; pub const FLOAT_MANTISSA_BITS: u32 = 23; pub const FLOAT_EXPONENT_BITS: u32 = 8; +const FLOAT_BIAS: i32 = 127; const FLOAT_POW5_INV_BITCOUNT: i32 = 59; const FLOAT_POW5_BITCOUNT: i32 = 61; @@ -178,59 +173,32 @@ unsafe { mul_shift(m, *FLOAT_POW5_SPLIT.get_unchecked(i as usize), j) } } -#[cfg_attr(feature = "no-panic", inline)] -pub fn decimal_length(v: u32) -> u32 { - // Function precondition: v is not a 10-digit number. - // (9 digits are sufficient for round-tripping.) - debug_assert!(v < 1000000000); - - if v >= 100000000 { - 9 - } else if v >= 10000000 { - 8 - } else if v >= 1000000 { - 7 - } else if v >= 100000 { - 6 - } else if v >= 10000 { - 5 - } else if v >= 1000 { - 4 - } else if v >= 100 { - 3 - } else if v >= 10 { - 2 - } else { - 1 - } -} - // A floating decimal representing m * 10^e. pub struct FloatingDecimal32 { pub mantissa: u32, + // Decimal exponent's range is -45 to 38 + // inclusive, and can fit in i16 if needed. pub exponent: i32, } #[cfg_attr(feature = "no-panic", inline)] pub fn f2d(ieee_mantissa: u32, ieee_exponent: u32) -> FloatingDecimal32 { - let bias = (1u32 << (FLOAT_EXPONENT_BITS - 1)) - 1; - let (e2, m2) = if ieee_exponent == 0 { ( // We subtract 2 so that the bounds computation has 2 additional bits. - 1 - bias as i32 - FLOAT_MANTISSA_BITS as i32 - 2, + 1 - FLOAT_BIAS - FLOAT_MANTISSA_BITS as i32 - 2, ieee_mantissa, ) } else { ( - ieee_exponent as i32 - bias as i32 - FLOAT_MANTISSA_BITS as i32 - 2, + ieee_exponent as i32 - FLOAT_BIAS - FLOAT_MANTISSA_BITS as i32 - 2, (1u32 << FLOAT_MANTISSA_BITS) | ieee_mantissa, ) }; let even = (m2 & 1) == 0; let accept_bounds = even; - // Step 2: Determine the interval of legal decimal representations. + // Step 2: Determine the interval of valid decimal representations. let mv = 4 * m2; let mp = 4 * m2 + 2; // Implicit bool -> int conversion. True is 1, false is 0. @@ -246,9 +214,9 @@ let mut vr_is_trailing_zeros = false; let mut last_removed_digit = 0u8; if e2 >= 0 { - let q = log10_pow2(e2) as u32; + let q = log10_pow2(e2); e10 = q as i32; - let k = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32) as i32 - 1; + let k = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1; let i = -e2 + q as i32 + k; vr = mul_pow5_inv_div_pow2(mv, q, i); vp = mul_pow5_inv_div_pow2(mp, q, i); @@ -257,7 +225,7 @@ // We need to know one removed digit even if we are not going to loop below. We could use // q = X - 1 above, except that would require 33 bits for the result, and we've found that // 32-bit arithmetic is faster even on 64-bit machines. - let l = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32 - 1) as i32 - 1; + let l = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32 - 1) - 1; last_removed_digit = (mul_pow5_inv_div_pow2(mv, q - 1, -e2 + q as i32 - 1 + l) % 10) as u8; } @@ -273,16 +241,16 @@ } } } else { - let q = log10_pow5(-e2) as u32; + let q = log10_pow5(-e2); e10 = q as i32 + e2; let i = -e2 - q as i32; - let k = pow5bits(i) as i32 - FLOAT_POW5_BITCOUNT; + let k = pow5bits(i) - FLOAT_POW5_BITCOUNT; let mut j = q as i32 - k; vr = mul_pow5_div_pow2(mv, i as u32, j); vp = mul_pow5_div_pow2(mp, i as u32, j); vm = mul_pow5_div_pow2(mm, i as u32, j); if q != 0 && (vp - 1) / 10 <= vm / 10 { - j = q as i32 - 1 - (pow5bits(i + 1) as i32 - FLOAT_POW5_BITCOUNT); + j = q as i32 - 1 - (pow5bits(i + 1) - FLOAT_POW5_BITCOUNT); last_removed_digit = (mul_pow5_div_pow2(mv, (i + 1) as u32, j) % 10) as u8; } if q <= 1 { @@ -302,8 +270,8 @@ } } - // Step 4: Find the shortest decimal representation in the interval of legal representations. - let mut removed = 0u32; + // Step 4: Find the shortest decimal representation in the interval of valid representations. + let mut removed = 0i32; let output = if vm_is_trailing_zeros || vr_is_trailing_zeros { // General case, which happens rarely (~4.0%). while vp / 10 > vm / 10 { @@ -346,150 +314,10 @@ // We need to take vr + 1 if vr is outside bounds or we need to round up. vr + (vr == vm || last_removed_digit >= 5) as u32 }; - let exp = e10 + removed as i32; + let exp = e10 + removed; FloatingDecimal32 { exponent: exp, mantissa: output, } } - -#[cfg_attr(feature = "no-panic", inline)] -unsafe fn to_chars(v: FloatingDecimal32, sign: bool, result: *mut u8) -> usize { - // Step 5: Print the decimal representation. - let mut index = 0isize; - if sign { - *result.offset(index) = b'-'; - index += 1; - } - - let mut output = v.mantissa; - let olength = decimal_length(output); - - // Print the decimal digits. - // The following code is equivalent to: - // for (uint32_t i = 0; i < olength - 1; ++i) { - // const uint32_t c = output % 10; output /= 10; - // result[index + olength - i] = (char) ('0' + c); - // } - // result[index] = '0' + output % 10; - let mut i = 0isize; - while output >= 10000 { - let c = output - 10000 * (output / 10000); - output /= 10000; - let c0 = (c % 100) << 1; - let c1 = (c / 100) << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c0 as usize), - result.offset(index + olength as isize - i - 1), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c1 as usize), - result.offset(index + olength as isize - i - 3), - 2, - ); - i += 4; - } - if output >= 100 { - let c = (output % 100) << 1; - output /= 100; - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked(c as usize), - result.offset(index + olength as isize - i - 1), - 2, - ); - i += 2; - } - if output >= 10 { - let c = output << 1; - // We can't use memcpy here: the decimal dot goes between these two digits. - *result.offset(index + olength as isize - i) = *DIGIT_TABLE.get_unchecked(c as usize + 1); - *result.offset(index) = *DIGIT_TABLE.get_unchecked(c as usize); - } else { - *result.offset(index) = b'0' + output as u8; - } - - // Print decimal point if needed. - if olength > 1 { - *result.offset(index + 1) = b'.'; - index += olength as isize + 1; - } else { - index += 1; - } - - // Print the exponent. - *result.offset(index) = b'E'; - index += 1; - let mut exp = v.exponent + olength as i32 - 1; - if exp < 0 { - *result.offset(index) = b'-'; - index += 1; - exp = -exp; - } - - if exp >= 10 { - ptr::copy_nonoverlapping( - DIGIT_TABLE.get_unchecked((2 * exp) as usize), - result.offset(index), - 2, - ); - index += 2; - } else { - *result.offset(index) = b'0' + exp as u8; - index += 1; - } - - debug_assert!(index <= 15); - index as usize -} - -/// Print f32 to the given buffer and return number of bytes written. Ryū's -/// original formatting. -/// -/// At most 15 bytes will be written. -/// -/// ## Special cases -/// -/// This function represents any NaN as `NaN`, positive infinity as `Infinity`, -/// and negative infinity as `-Infinity`. -/// -/// ## Safety -/// -/// The `result` pointer argument must point to sufficiently many writable bytes -/// to hold Ryū's representation of `f`. -/// -/// ## Example -/// -/// ```rust -/// let f = 1.234f32; -/// -/// unsafe { -/// let mut buffer: [u8; 15] = std::mem::uninitialized(); -/// let n = ryu::raw::f2s_buffered_n(f, &mut buffer[0]); -/// let s = std::str::from_utf8_unchecked(&buffer[..n]); -/// assert_eq!(s, "1.234E0"); -/// } -/// ``` -#[cfg_attr(must_use_return, must_use)] -#[cfg_attr(feature = "no-panic", no_panic)] -pub unsafe fn f2s_buffered_n(f: f32, result: *mut u8) -> usize { - // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. - let bits = mem::transmute::(f); - - // Decode bits into sign, mantissa, and exponent. - let ieee_sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0; - let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1); - let ieee_exponent = - ((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32; - - // Case distinction; exit early for the easy cases. - if ieee_exponent == ((1u32 << FLOAT_EXPONENT_BITS) - 1) - || (ieee_exponent == 0 && ieee_mantissa == 0) - { - return copy_special_str(result, ieee_sign, ieee_exponent != 0, ieee_mantissa != 0); - } - - let v = f2d(ieee_mantissa, ieee_exponent); - to_chars(v, ieee_sign, result) -} diff -Nru rust-ryu-0.2.7/src/lib.rs rust-ryu-1.0.0/src/lib.rs --- rust-ryu-0.2.7/src/lib.rs 2018-11-10 22:33:44.000000000 +0000 +++ rust-ryu-1.0.0/src/lib.rs 2019-06-11 21:14:33.000000000 +0000 @@ -6,40 +6,86 @@ //! available under the creative commons CC-BY-SA license. //! //! This Rust implementation is a line-by-line port of Ulf Adams' implementation -//! in C, [https://github.com/ulfjack/ryu][upstream]. The [`ryu::raw`][raw] -//! module exposes exactly the API and formatting of the C implementation as -//! unsafe pure Rust functions. There is additionally a safe API as demonstrated -//! in the example code below. The safe API uses the same underlying Ryū -//! algorithm but diverges from the formatting of the C implementation to -//! produce more human-readable output, for example `0.3` rather than `3E-1`. +//! in C, [https://github.com/ulfjack/ryu][upstream]. //! //! [paper]: https://dl.acm.org/citation.cfm?id=3192369 //! [upstream]: https://github.com/ulfjack/ryu -//! [raw]: raw/index.html //! -//! # Examples -//! -//! ```rust -//! extern crate ryu; +//! # Example //! +//! ```edition2018 //! fn main() { //! let mut buffer = ryu::Buffer::new(); //! let printed = buffer.format(1.234); //! assert_eq!(printed, "1.234"); //! } //! ``` +//! +//! ## Performance +//! +//! You can run upstream's benchmarks with: +//! +//! ```console +//! $ git clone https://github.com/ulfjack/ryu c-ryu +//! $ cd c-ryu +//! $ bazel run -c opt //ryu/benchmark +//! ``` +//! +//! And the same benchmark against our implementation with: +//! +//! ```console +//! $ git clone https://github.com/dtolnay/ryu rust-ryu +//! $ cd rust-ryu +//! $ cargo run --example upstream_benchmark --release +//! ``` +//! +//! These benchmarks measure the average time to print a 32-bit float and average +//! time to print a 64-bit float, where the inputs are distributed as uniform random +//! bit patterns 32 and 64 bits wide. +//! +//! The upstream C code, the unsafe direct Rust port, and the safe pretty Rust API +//! all perform the same, taking around 21 nanoseconds to format a 32-bit float and +//! 31 nanoseconds to format a 64-bit float. +//! +//! There is also a Rust-specific benchmark comparing this implementation to the +//! standard library which you can run with: +//! +//! ```console +//! $ cargo bench +//! ``` +//! +//! The benchmark shows Ryu approximately 4-10x faster than the standard library +//! across a range of f32 and f64 inputs. Measurements are in nanoseconds per +//! iteration; smaller is better. +//! +//! | type=f32 | 0.0 | 0.1234 | 2.718281828459045 | f32::MAX | +//! |:--------:|:----:|:------:|:-----------------:|:--------:| +//! | RYU | 3ns | 28ns | 23ns | 22ns | +//! | STD | 40ns | 106ns | 128ns | 110ns | +//! +//! | type=f64 | 0.0 | 0.1234 | 2.718281828459045 | f64::MAX | +//! |:--------:|:----:|:------:|:-----------------:|:--------:| +//! | RYU | 3ns | 50ns | 35ns | 32ns | +//! | STD | 39ns | 105ns | 128ns | 202ns | +//! +//! ## Formatting +//! +//! This library tends to produce more human-readable output than the standard +//! library's to\_string, which never uses scientific notation. Here are two +//! examples: +//! +//! - *ryu:* 1.23e40, *std:* 12300000000000000000000000000000000000000 +//! - *ryu:* 1.23e-40, *std:* 0.000000000000000000000000000000000000000123 +//! +//! Both libraries print short decimals such as 0.0000123 without scientific +//! notation. #![no_std] -#![doc(html_root_url = "https://docs.rs/ryu/0.2.7")] +#![doc(html_root_url = "https://docs.rs/ryu/1.0.0")] +#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))] #![cfg_attr( feature = "cargo-clippy", - allow( - cast_lossless, - cyclomatic_complexity, - many_single_char_names, - needless_pass_by_value, - unreadable_literal, - ) + allow(cast_lossless, many_single_char_names, unreadable_literal,) )] #[cfg(feature = "no-panic")] @@ -59,10 +105,7 @@ pub use buffer::{Buffer, Float}; -/// Unsafe functions that exactly mirror the API of the C implementation of Ryū. +/// Unsafe functions that mirror the API of the C implementation of Ryū. pub mod raw { - pub use d2s::d2s_buffered_n; - pub use f2s::f2s_buffered_n; - pub use pretty::d2s_buffered_n as pretty_d2s_buffered_n; - pub use pretty::f2s_buffered_n as pretty_f2s_buffered_n; + pub use pretty::{format32, format64}; } diff -Nru rust-ryu-0.2.7/src/pretty/mod.rs rust-ryu-1.0.0/src/pretty/mod.rs --- rust-ryu-0.2.7/src/pretty/mod.rs 2018-11-10 21:58:39.000000000 +0000 +++ rust-ryu-1.0.0/src/pretty/mod.rs 2019-06-11 21:13:49.000000000 +0000 @@ -5,16 +5,15 @@ use self::exponent::*; use self::mantissa::*; +use common; use d2s; use d2s::*; -use f2s; use f2s::*; #[cfg(feature = "no-panic")] use no_panic::no_panic; -/// Print f64 to the given buffer and return number of bytes written. Human -/// readable formatting. +/// Print f64 to the given buffer and return number of bytes written. /// /// At most 24 bytes will be written. /// @@ -38,19 +37,20 @@ /// /// ## Example /// -/// ```rust +/// ```edition2018 /// let f = 1.234f64; /// /// unsafe { /// let mut buffer: [u8; 24] = std::mem::uninitialized(); -/// let n = ryu::raw::pretty_d2s_buffered_n(f, &mut buffer[0]); -/// let s = std::str::from_utf8_unchecked(&buffer[..n]); -/// assert_eq!(s, "1.234"); +/// let len = ryu::raw::format64(f, buffer.as_mut_ptr()); +/// let slice = std::slice::from_raw_parts(buffer.as_ptr(), len); +/// let print = std::str::from_utf8_unchecked(slice); +/// assert_eq!(print, "1.234"); /// } /// ``` #[cfg_attr(must_use_return, must_use)] #[cfg_attr(feature = "no-panic", no_panic)] -pub unsafe fn d2s_buffered_n(f: f64, result: *mut u8) -> usize { +pub unsafe fn format64(f: f64, result: *mut u8) -> usize { let bits = mem::transmute::(f); let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1); @@ -70,7 +70,7 @@ let v = d2d(ieee_mantissa, ieee_exponent); - let length = d2s::decimal_length(v.mantissa) as isize; + let length = d2s::decimal_length17(v.mantissa) as isize; let k = v.exponent as isize; let kk = length + k; // 10^(kk-1) <= v < 10^kk debug_assert!(k >= -324); @@ -118,8 +118,7 @@ } } -/// Print f32 to the given buffer and return number of bytes written. Human -/// readable formatting. +/// Print f32 to the given buffer and return number of bytes written. /// /// At most 16 bytes will be written. /// @@ -143,19 +142,20 @@ /// /// ## Example /// -/// ```rust +/// ```edition2018 /// let f = 1.234f32; /// /// unsafe { /// let mut buffer: [u8; 16] = std::mem::uninitialized(); -/// let n = ryu::raw::pretty_f2s_buffered_n(f, &mut buffer[0]); -/// let s = std::str::from_utf8_unchecked(&buffer[..n]); -/// assert_eq!(s, "1.234"); +/// let len = ryu::raw::format32(f, buffer.as_mut_ptr()); +/// let slice = std::slice::from_raw_parts(buffer.as_ptr(), len); +/// let print = std::str::from_utf8_unchecked(slice); +/// assert_eq!(print, "1.234"); /// } /// ``` #[cfg_attr(must_use_return, must_use)] #[cfg_attr(feature = "no-panic", no_panic)] -pub unsafe fn f2s_buffered_n(f: f32, result: *mut u8) -> usize { +pub unsafe fn format32(f: f32, result: *mut u8) -> usize { let bits = mem::transmute::(f); let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0; let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1); @@ -175,7 +175,7 @@ let v = f2d(ieee_mantissa, ieee_exponent); - let length = f2s::decimal_length(v.mantissa) as isize; + let length = common::decimal_length9(v.mantissa) as isize; let k = v.exponent as isize; let kk = length + k; // 10^(kk-1) <= v < 10^kk debug_assert!(k >= -45); diff -Nru rust-ryu-0.2.7/tests/d2s_test.rs rust-ryu-1.0.0/tests/d2s_test.rs --- rust-ryu-0.2.7/tests/d2s_test.rs 2018-10-07 00:32:20.000000000 +0000 +++ rust-ryu-1.0.0/tests/d2s_test.rs 2019-06-11 21:13:49.000000000 +0000 @@ -24,14 +24,7 @@ #[macro_use] mod macros; -use std::{f64, str}; - -fn print(f: f64) -> String { - let mut bytes = [0u8; 24]; - let n = unsafe { ryu::raw::d2s_buffered_n(f, &mut bytes[0]) }; - let s = str::from_utf8(&bytes[..n]).unwrap(); - s.to_owned() -} +use std::f64; fn pretty(f: f64) -> String { ryu::Buffer::new().format(f).to_owned() @@ -45,26 +38,23 @@ #[test] fn test_ryu() { - check!(3E-1, 0.3); - check!(1.234E15, 1234000000000000.0); - check!(1.234E16, 1.234e16); - check!(2.71828E0, 2.71828); - check!(1.1E128, 1.1e128); - check!(1.1E-64, 1.1e-64); - check!(2.718281828459045E0, 2.718281828459045); - check!(5E-324, 5e-324); - check!(1.7976931348623157E308, 1.7976931348623157e308); + check!(0.3); + check!(1234000000000000.0); + check!(1.234e16); + check!(2.71828); + check!(1.1e128); + check!(1.1e-64); + check!(2.718281828459045); + check!(5e-324); + check!(1.7976931348623157e308); } #[test] fn test_random() { - let mut bytes = [0u8; 24]; let mut buffer = ryu::Buffer::new(); for _ in 0..1000000 { - let f = rand::random(); - let n = unsafe { ryu::raw::d2s_buffered_n(f, &mut bytes[0]) }; - assert_eq!(f, str::from_utf8(&bytes[..n]).unwrap().parse().unwrap()); - assert_eq!(f, buffer.format(f).parse().unwrap()); + let f: f64 = rand::random(); + assert_eq!(f, buffer.format_finite(f).parse().unwrap()); } } @@ -73,49 +63,49 @@ for i in 0u64..1 << 23 { let f = f64::from_bits((((1 << 11) - 1) << 52) + (i << 29)); assert!(!f.is_finite(), "f={}", f); - ryu::Buffer::new().format(f); + ryu::Buffer::new().format_finite(f); } } #[test] fn test_basic() { - check!(0E0, 0.0); - check!(-0E0, -0.0); - check!(1E0, 1.0); - check!(-1E0, -1.0); - assert_eq!(print(f64::NAN), "NaN"); - assert_eq!(print(f64::INFINITY), "Infinity"); - assert_eq!(print(f64::NEG_INFINITY), "-Infinity"); + check!(0.0); + check!(-0.0); + check!(1.0); + check!(-1.0); + assert_eq!(pretty(f64::NAN), "NaN"); + assert_eq!(pretty(f64::INFINITY), "inf"); + assert_eq!(pretty(f64::NEG_INFINITY), "-inf"); } #[test] fn test_switch_to_subnormal() { - check!(2.2250738585072014E-308, 2.2250738585072014e-308); + check!(2.2250738585072014e-308); } #[test] fn test_min_and_max() { assert_eq!(f64::from_bits(0x7fefffffffffffff), 1.7976931348623157e308); - check!(1.7976931348623157E308, 1.7976931348623157e308); + check!(1.7976931348623157e308); assert_eq!(f64::from_bits(1), 5e-324); - check!(5E-324, 5e-324); + check!(5e-324); } #[test] fn test_lots_of_trailing_zeros() { - check!(2.9802322387695312E-8, 2.9802322387695312e-8); + check!(2.9802322387695312e-8); } #[test] fn test_regression() { - check!(-2.109808898695963E16, -2.109808898695963e16); - check!(4.940656E-318, 4.940656e-318); - check!(1.18575755E-316, 1.18575755e-316); - check!(2.989102097996E-312, 2.989102097996e-312); - check!(9.0608011534336E15, 9060801153433600.0); - check!(4.708356024711512E18, 4.708356024711512e18); - check!(9.409340012568248E18, 9.409340012568248e18); - check!(1.2345678E0, 1.2345678); + check!(-2.109808898695963e16); + check!(4.940656e-318); + check!(1.18575755e-316); + check!(2.989102097996e-312); + check!(9060801153433600.0); + check!(4.708356024711512e18); + check!(9.409340012568248e18); + check!(1.2345678); } #[test] @@ -124,39 +114,39 @@ // 5 that fits, and an exponent that causes the computation for q to result // in 22, which is a corner case for Ryu. assert_eq!(f64::from_bits(0x4830F0CF064DD592), 5.764607523034235e39); - check!(5.764607523034235E39, 5.764607523034235e39); + check!(5.764607523034235e39); assert_eq!(f64::from_bits(0x4840F0CF064DD592), 1.152921504606847e40); - check!(1.152921504606847E40, 1.152921504606847e40); + check!(1.152921504606847e40); assert_eq!(f64::from_bits(0x4850F0CF064DD592), 2.305843009213694e40); - check!(2.305843009213694E40, 2.305843009213694e40); + check!(2.305843009213694e40); } #[test] fn test_output_length() { - check!(1E0, 1.0); // already tested in Basic - check!(1.2E0, 1.2); - check!(1.23E0, 1.23); - check!(1.234E0, 1.234); - check!(1.2345E0, 1.2345); - check!(1.23456E0, 1.23456); - check!(1.234567E0, 1.234567); - check!(1.2345678E0, 1.2345678); // already tested in Regression - check!(1.23456789E0, 1.23456789); - check!(1.234567895E0, 1.234567895); // 1.234567890 would be trimmed - check!(1.2345678901E0, 1.2345678901); - check!(1.23456789012E0, 1.23456789012); - check!(1.234567890123E0, 1.234567890123); - check!(1.2345678901234E0, 1.2345678901234); - check!(1.23456789012345E0, 1.23456789012345); - check!(1.234567890123456E0, 1.234567890123456); - check!(1.2345678901234567E0, 1.2345678901234567); + check!(1.0); // already tested in Basic + check!(1.2); + check!(1.23); + check!(1.234); + check!(1.2345); + check!(1.23456); + check!(1.234567); + check!(1.2345678); // already tested in Regression + check!(1.23456789); + check!(1.234567895); // 1.234567890 would be trimmed + check!(1.2345678901); + check!(1.23456789012); + check!(1.234567890123); + check!(1.2345678901234); + check!(1.23456789012345); + check!(1.234567890123456); + check!(1.2345678901234567); // Test 32-bit chunking - check!(4.294967294E0, 4.294967294); // 2^32 - 2 - check!(4.294967295E0, 4.294967295); // 2^32 - 1 - check!(4.294967296E0, 4.294967296); // 2^32 - check!(4.294967297E0, 4.294967297); // 2^32 + 1 - check!(4.294967298E0, 4.294967298); // 2^32 + 2 + check!(4.294967294); // 2^32 - 2 + check!(4.294967295); // 2^32 - 1 + check!(4.294967296); // 2^32 + check!(4.294967297); // 2^32 + 1 + check!(4.294967298); // 2^32 + 2 } // Test min, max shift values in shiftright128 @@ -169,56 +159,163 @@ // 64-bit opt-size=0: 50 <= dist <= 50 // 64-bit opt-size=1: 30 <= dist <= 50 assert_eq!(1.7800590868057611E-307, ieee_parts_to_double(false, 4, 0)); - check!(1.7800590868057611E-307, 1.7800590868057611e-307); + check!(1.7800590868057611e-307); // 32-bit opt-size=0: 49 <= dist <= 49 // 32-bit opt-size=1: 28 <= dist <= 49 // 64-bit opt-size=0: 50 <= dist <= 50 // 64-bit opt-size=1: 28 <= dist <= 50 - assert_eq!(2.8480945388892175E-306, ieee_parts_to_double(false, 6, max_mantissa)); - check!(2.8480945388892175E-306, 2.8480945388892175e-306); + assert_eq!( + 2.8480945388892175E-306, + ieee_parts_to_double(false, 6, max_mantissa) + ); + check!(2.8480945388892175e-306); // 32-bit opt-size=0: 52 <= dist <= 53 // 32-bit opt-size=1: 2 <= dist <= 53 // 64-bit opt-size=0: 53 <= dist <= 53 // 64-bit opt-size=1: 2 <= dist <= 53 assert_eq!(2.446494580089078E-296, ieee_parts_to_double(false, 41, 0)); - check!(2.446494580089078E-296, 2.446494580089078e-296); + check!(2.446494580089078e-296); // 32-bit opt-size=0: 52 <= dist <= 52 // 32-bit opt-size=1: 2 <= dist <= 52 // 64-bit opt-size=0: 53 <= dist <= 53 // 64-bit opt-size=1: 2 <= dist <= 53 - assert_eq!(4.8929891601781557E-296, ieee_parts_to_double(false, 40, max_mantissa)); - check!(4.8929891601781557E-296, 4.8929891601781557e-296); + assert_eq!( + 4.8929891601781557E-296, + ieee_parts_to_double(false, 40, max_mantissa) + ); + check!(4.8929891601781557e-296); // 32-bit opt-size=0: 57 <= dist <= 58 // 32-bit opt-size=1: 57 <= dist <= 58 // 64-bit opt-size=0: 58 <= dist <= 58 // 64-bit opt-size=1: 58 <= dist <= 58 assert_eq!(1.8014398509481984E16, ieee_parts_to_double(false, 1077, 0)); - check!(1.8014398509481984E16, 1.8014398509481984e16); + check!(1.8014398509481984e16); // 32-bit opt-size=0: 57 <= dist <= 57 // 32-bit opt-size=1: 57 <= dist <= 57 // 64-bit opt-size=0: 58 <= dist <= 58 // 64-bit opt-size=1: 58 <= dist <= 58 - assert_eq!(3.6028797018963964E16, ieee_parts_to_double(false, 1076, max_mantissa)); - check!(3.6028797018963964E16, 3.6028797018963964e16); + assert_eq!( + 3.6028797018963964E16, + ieee_parts_to_double(false, 1076, max_mantissa) + ); + check!(3.6028797018963964e16); // 32-bit opt-size=0: 51 <= dist <= 52 // 32-bit opt-size=1: 51 <= dist <= 59 // 64-bit opt-size=0: 52 <= dist <= 52 // 64-bit opt-size=1: 52 <= dist <= 59 assert_eq!(2.900835519859558E-216, ieee_parts_to_double(false, 307, 0)); - check!(2.900835519859558E-216, 2.900835519859558e-216); + check!(2.900835519859558e-216); // 32-bit opt-size=0: 51 <= dist <= 51 // 32-bit opt-size=1: 51 <= dist <= 59 // 64-bit opt-size=0: 52 <= dist <= 52 // 64-bit opt-size=1: 52 <= dist <= 59 - assert_eq!(5.801671039719115E-216, ieee_parts_to_double(false, 306, max_mantissa)); - check!(5.801671039719115E-216, 5.801671039719115e-216); + assert_eq!( + 5.801671039719115E-216, + ieee_parts_to_double(false, 306, max_mantissa) + ); + check!(5.801671039719115e-216); // https://github.com/ulfjack/ryu/commit/19e44d16d80236f5de25800f56d82606d1be00b9#commitcomment-30146483 // 32-bit opt-size=0: 49 <= dist <= 49 // 32-bit opt-size=1: 44 <= dist <= 49 // 64-bit opt-size=0: 50 <= dist <= 50 // 64-bit opt-size=1: 44 <= dist <= 50 - assert_eq!(3.196104012172126E-27, ieee_parts_to_double(false, 934, 0x000FA7161A4D6E0C)); - check!(3.196104012172126E-27, 3.196104012172126e-27); + assert_eq!( + 3.196104012172126E-27, + ieee_parts_to_double(false, 934, 0x000FA7161A4D6E0C) + ); + check!(3.196104012172126e-27); +} + +#[test] +fn test_small_integers() { + check!(9007199254740991.0); // 2^53-1 + check!(9007199254740992.0); // 2^53 + + check!(1.0); + check!(12.0); + check!(123.0); + check!(1234.0); + check!(12345.0); + check!(123456.0); + check!(1234567.0); + check!(12345678.0); + check!(123456789.0); + check!(1234567890.0); + check!(1234567895.0); + check!(12345678901.0); + check!(123456789012.0); + check!(1234567890123.0); + check!(12345678901234.0); + check!(123456789012345.0); + check!(1234567890123456.0); + + // 10^i + check!(1.0); + check!(10.0); + check!(100.0); + check!(1000.0); + check!(10000.0); + check!(100000.0); + check!(1000000.0); + check!(10000000.0); + check!(100000000.0); + check!(1000000000.0); + check!(10000000000.0); + check!(100000000000.0); + check!(1000000000000.0); + check!(10000000000000.0); + check!(100000000000000.0); + check!(1000000000000000.0); + + // 10^15 + 10^i + check!(1000000000000001.0); + check!(1000000000000010.0); + check!(1000000000000100.0); + check!(1000000000001000.0); + check!(1000000000010000.0); + check!(1000000000100000.0); + check!(1000000001000000.0); + check!(1000000010000000.0); + check!(1000000100000000.0); + check!(1000001000000000.0); + check!(1000010000000000.0); + check!(1000100000000000.0); + check!(1001000000000000.0); + check!(1010000000000000.0); + check!(1100000000000000.0); + + // Largest power of 2 <= 10^(i+1) + check!(8.0); + check!(64.0); + check!(512.0); + check!(8192.0); + check!(65536.0); + check!(524288.0); + check!(8388608.0); + check!(67108864.0); + check!(536870912.0); + check!(8589934592.0); + check!(68719476736.0); + check!(549755813888.0); + check!(8796093022208.0); + check!(70368744177664.0); + check!(562949953421312.0); + check!(9007199254740992.0); + + // 1000 * (Largest power of 2 <= 10^(i+1)) + check!(8000.0); + check!(64000.0); + check!(512000.0); + check!(8192000.0); + check!(65536000.0); + check!(524288000.0); + check!(8388608000.0); + check!(67108864000.0); + check!(536870912000.0); + check!(8589934592000.0); + check!(68719476736000.0); + check!(549755813888000.0); + check!(8796093022208000.0); } diff -Nru rust-ryu-0.2.7/tests/exhaustive.rs rust-ryu-1.0.0/tests/exhaustive.rs --- rust-ryu-0.2.7/tests/exhaustive.rs 2018-11-10 21:52:59.000000000 +0000 +++ rust-ryu-1.0.0/tests/exhaustive.rs 2019-06-11 21:13:49.000000000 +0000 @@ -38,9 +38,9 @@ if !f.is_finite() { continue; } - let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) }; + let n = unsafe { ryu::raw::format32(f, &mut bytes[0]) }; assert_eq!(Ok(Ok(f)), str::from_utf8(&bytes[..n]).map(str::parse)); - assert_eq!(Ok(f), buffer.format(f).parse()); + assert_eq!(Ok(f), buffer.format_finite(f).parse()); } let increment = (max - min + 1) as usize; diff -Nru rust-ryu-0.2.7/tests/f2s_test.rs rust-ryu-1.0.0/tests/f2s_test.rs --- rust-ryu-0.2.7/tests/f2s_test.rs 2018-08-07 18:42:40.000000000 +0000 +++ rust-ryu-1.0.0/tests/f2s_test.rs 2019-06-11 21:13:49.000000000 +0000 @@ -24,14 +24,7 @@ #[macro_use] mod macros; -use std::{f32, str}; - -fn print(f: f32) -> String { - let mut bytes = [0u8; 24]; - let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) }; - let s = str::from_utf8(&bytes[..n]).unwrap(); - s.to_owned() -} +use std::f32; fn pretty(f: f32) -> String { ryu::Buffer::new().format(f).to_owned() @@ -39,27 +32,24 @@ #[test] fn test_ryu() { - check!(3E-1, 0.3); - check!(1.234E12, 1234000000000.0); - check!(1.234E13, 1.234e13); - check!(2.71828E0, 2.71828); - check!(1.1E32, 1.1e32); - check!(1.1E-32, 1.1e-32); - check!(2.7182817E0, 2.7182817); - check!(1E-45, 1e-45); - check!(3.4028235E38, 3.4028235e38); - check!(-1.234E-3, -0.001234); + check!(0.3); + check!(1234000000000.0); + check!(1.234e13); + check!(2.71828); + check!(1.1e32); + check!(1.1e-32); + check!(2.7182817); + check!(1e-45); + check!(3.4028235e38); + check!(-0.001234); } #[test] fn test_random() { - let mut bytes = [0u8; 24]; let mut buffer = ryu::Buffer::new(); for _ in 0..1000000 { - let f = rand::random(); - let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) }; - assert_eq!(f, str::from_utf8(&bytes[..n]).unwrap().parse().unwrap()); - assert_eq!(f, buffer.format(f).parse().unwrap()); + let f: f32 = rand::random(); + assert_eq!(f, buffer.format_finite(f).parse().unwrap()); } } @@ -68,41 +58,41 @@ for i in 0u32..1 << 23 { let f = f32::from_bits((((1 << 8) - 1) << 23) + i); assert!(!f.is_finite(), "f={}", f); - ryu::Buffer::new().format(f); + ryu::Buffer::new().format_finite(f); } } #[test] fn test_basic() { - check!(0E0, 0.0); - check!(-0E0, -0.0); - check!(1E0, 1.0); - check!(-1E0, -1.0); - assert_eq!(print(f32::NAN), "NaN"); - assert_eq!(print(f32::INFINITY), "Infinity"); - assert_eq!(print(f32::NEG_INFINITY), "-Infinity"); + check!(0.0); + check!(-0.0); + check!(1.0); + check!(-1.0); + assert_eq!(pretty(f32::NAN), "NaN"); + assert_eq!(pretty(f32::INFINITY), "inf"); + assert_eq!(pretty(f32::NEG_INFINITY), "-inf"); } #[test] fn test_switch_to_subnormal() { - check!(1.1754944E-38, 1.1754944e-38); + check!(1.1754944e-38); } #[test] fn test_min_and_max() { assert_eq!(f32::from_bits(0x7f7fffff), 3.4028235e38); - check!(3.4028235E38, 3.4028235e38); + check!(3.4028235e38); assert_eq!(f32::from_bits(1), 1e-45); - check!(1E-45, 1e-45); + check!(1e-45); } // Check that we return the exact boundary if it is the shortest // representation, but only if the original floating point number is even. #[test] fn test_boundary_round_even() { - check!(3.355445E7, 33554450.0); - check!(9E9, 9000000000.0); - check!(3.436672E10, 34366720000.0); + check!(33554450.0); + check!(9000000000.0); + check!(34366720000.0); } // If the exact value is exactly halfway between two shortest representations, @@ -110,50 +100,50 @@ // last two digits are ...2|5 or ...7|5, and we cut off the 5. #[test] fn test_exact_value_round_even() { - check!(3.0540412E5, 305404.12); - check!(8.0990312E3, 8099.0312); + check!(305404.12); + check!(8099.0312); } #[test] fn test_lots_of_trailing_zeros() { // Pattern for the first test: 00111001100000000000000000000000 - check!(2.4414062E-4, 0.00024414062); - check!(2.4414062E-3, 0.0024414062); - check!(4.3945312E-3, 0.0043945312); - check!(6.3476562E-3, 0.0063476562); + check!(0.00024414062); + check!(0.0024414062); + check!(0.0043945312); + check!(0.0063476562); } #[test] fn test_regression() { - check!(4.7223665E21, 4.7223665e21); - check!(8.388608E6, 8388608.0); - check!(1.6777216E7, 16777216.0); - check!(3.3554436E7, 33554436.0); - check!(6.7131496E7, 67131496.0); - check!(1.9310392E-38, 1.9310392e-38); - check!(-2.47E-43, -2.47e-43); - check!(1.993244E-38, 1.993244e-38); - check!(4.1039004E3, 4103.9004); - check!(5.3399997E9, 5339999700.0); - check!(6.0898E-39, 6.0898e-39); - check!(1.0310042E-3, 0.0010310042); - check!(2.882326E17, 2.882326e17); - check!(7.038531E-26, 7.038531e-26); - check!(9.223404E17, 9.223404e17); - check!(6.710887E7, 67108870.0); - check!(1E-44, 1e-44); - check!(2.816025E14, 2.816025e14); - check!(9.223372E18, 9.223372e18); - check!(1.5846086E29, 1.5846086e29); - check!(1.1811161E19, 1.1811161e19); - check!(5.368709E18, 5.368709e18); - check!(4.6143166E18, 4.6143166e18); - check!(7.812537E-3, 0.007812537); - check!(1E-45, 1e-45); - check!(1.18697725E20, 1.18697725e20); - check!(1.00014165E-36, 1.00014165e-36); - check!(2E2, 200.0); - check!(3.3554432E7, 33554432.0); + check!(4.7223665e21); + check!(8388608.0); + check!(16777216.0); + check!(33554436.0); + check!(67131496.0); + check!(1.9310392e-38); + check!(-2.47e-43); + check!(1.993244e-38); + check!(4103.9004); + check!(5339999700.0); + check!(6.0898e-39); + check!(0.0010310042); + check!(2.882326e17); + check!(7.038531e-26); + check!(9.223404e17); + check!(67108870.0); + check!(1e-44); + check!(2.816025e14); + check!(9.223372e18); + check!(1.5846086e29); + check!(1.1811161e19); + check!(5.368709e18); + check!(4.6143166e18); + check!(0.007812537); + check!(1e-45); + check!(1.18697725e20); + check!(1.00014165e-36); + check!(200.0); + check!(33554432.0); } #[test] @@ -162,22 +152,22 @@ // and an exponent that causes the computation for q to result in 10, which // is a corner case for Ryu. assert_eq!(f32::from_bits(0x5D1502F9), 6.7108864e17); - check!(6.7108864E17, 6.7108864e17); + check!(6.7108864e17); assert_eq!(f32::from_bits(0x5D9502F9), 1.3421773e18); - check!(1.3421773E18, 1.3421773e18); + check!(1.3421773e18); assert_eq!(f32::from_bits(0x5E1502F9), 2.6843546e18); - check!(2.6843546E18, 2.6843546e18); + check!(2.6843546e18); } #[test] fn test_output_length() { - check!(1E0, 1.0); // already tested in Basic - check!(1.2E0, 1.2); - check!(1.23E0, 1.23); - check!(1.234E0, 1.234); - check!(1.2345E0, 1.2345); - check!(1.23456E0, 1.23456); - check!(1.234567E0, 1.234567); - check!(1.2345678E0, 1.2345678); - check!(1.23456735E-36, 1.23456735e-36); + check!(1.0); // already tested in Basic + check!(1.2); + check!(1.23); + check!(1.234); + check!(1.2345); + check!(1.23456); + check!(1.234567); + check!(1.2345678); + check!(1.23456735e-36); } diff -Nru rust-ryu-0.2.7/tests/macros/mod.rs rust-ryu-1.0.0/tests/macros/mod.rs --- rust-ryu-0.2.7/tests/macros/mod.rs 2018-08-07 18:42:40.000000000 +0000 +++ rust-ryu-1.0.0/tests/macros/mod.rs 2019-06-11 21:13:49.000000000 +0000 @@ -1,12 +1,8 @@ macro_rules! check { - ($ryu:tt, $pretty:tt) => { - assert_eq!($ryu, $pretty); - assert_eq!(print($ryu), stringify!($ryu)); - assert_eq!(pretty($pretty), stringify!($pretty)); + ($f:tt) => { + assert_eq!(pretty($f), stringify!($f)); }; - (-$ryu:tt, -$pretty:tt) => { - assert_eq!(-$ryu, -$pretty); - assert_eq!(print(-$ryu), concat!("-", stringify!($ryu))); - assert_eq!(pretty(-$pretty), concat!("-", stringify!($pretty))); + (-$f:tt) => { + assert_eq!(pretty(-$f), concat!("-", stringify!($f))); }; } diff -Nru rust-ryu-0.2.7/.travis.yml rust-ryu-1.0.0/.travis.yml --- rust-ryu-0.2.7/.travis.yml 2018-08-14 16:44:18.000000000 +0000 +++ rust-ryu-1.0.0/.travis.yml 2019-06-11 21:13:49.000000000 +0000 @@ -1,12 +1,17 @@ language: rust -rust: - - nightly - - beta - - stable - matrix: include: + - rust: nightly + script: + - cargo test + - cargo build --tests --features no-panic --release + - rust: beta + script: + - cargo test + - rust: stable + script: + - cargo test - rust: 1.15.0 script: - cargo build