diff --git a/Cargo.toml b/Cargo.toml index 62ce258..c245b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "ryu" version = "0.0.0" authors = ["David Tolnay "] +build = "build.rs" publish = false [dependencies] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a0474a4 --- /dev/null +++ b/build.rs @@ -0,0 +1,48 @@ +use std::env; +use std::process::Command; +use std::str::{self, FromStr}; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + // 128-bit integers stabilized in Rust 1.26: + // https://blog.rust-lang.org/2018/05/10/Rust-1.26.html + if minor >= 26 { + println!("cargo:rustc-cfg=integer128"); + } +} + +fn rustc_minor_version() -> Option { + let rustc = match env::var_os("RUSTC") { + Some(rustc) => rustc, + None => return None, + }; + + let output = match Command::new(rustc).arg("--version").output() { + Ok(output) => output, + Err(_) => return None, + }; + + let version = match str::from_utf8(&output.stdout) { + Ok(version) => version, + Err(_) => return None, + }; + + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + + let next = match pieces.next() { + Some(next) => next, + None => return None, + }; + + u32::from_str(next).ok() +} diff --git a/src/d2s.rs b/src/d2s.rs index e538e19..4084755 100644 --- a/src/d2s.rs +++ b/src/d2s.rs @@ -23,6 +23,8 @@ use core::{mem, ptr}; use common::*; use d2s_full_table::*; use digit_table::*; +#[cfg(not(integer128))] +use mulshift128::*; #[cfg(feature = "no-panic")] use no_panic::no_panic; @@ -62,6 +64,7 @@ fn multiple_of_power_of_2(value: u64, p: u32) -> bool { (value & ((1u64 << (p - 1)) - 1)) == 0 } +#[cfg(integer128)] #[cfg_attr(feature = "no-panic", inline)] fn mul_shift(m: u64, mul: &(u64, u64), j: u32) -> u64 { let b0 = m as u128 * mul.0 as u128; @@ -69,6 +72,7 @@ fn mul_shift(m: u64, mul: &(u64, u64), j: u32) -> u64 { (((b0 >> 64) + b2) >> (j - 64)) as u64 } +#[cfg(integer128)] #[cfg_attr(feature = "no-panic", inline)] fn mul_shift_all( m: u64, @@ -83,6 +87,46 @@ fn mul_shift_all( mul_shift(4 * m, mul, j) } +#[cfg(not(integer128))] +#[cfg_attr(feature = "no-panic", inline)] +fn mul_shift_all( + mut m: u64, + mul: &(u64, u64), + j: u32, + vp: &mut u64, + vm: &mut u64, + mm_shift: u32, +) -> u64 { + m <<= 1; + // m is maximum 55 bits + let (lo, tmp) = umul128(m, mul.0); + let (mut mid, mut hi) = umul128(m, mul.1); + mid = mid.wrapping_add(tmp); + hi = hi.wrapping_add((mid < tmp) as u64); // overflow into hi + + let lo2 = lo.wrapping_add(mul.0); + let mid2 = mid.wrapping_add(mul.1).wrapping_add((lo2 < lo) as u64); + let hi2 = hi.wrapping_add((mid2 < mid) as u64); + *vp = shiftright128(mid2, hi2, j - 64 - 1); + + if mm_shift == 1 { + let lo3 = lo.wrapping_sub(mul.0); + let mid3 = mid.wrapping_sub(mul.1).wrapping_sub((lo3 > lo) as u64); + let hi3 = hi - (mid3 > mid) as u64; + *vm = shiftright128(mid3, hi3, j - 64 - 1); + } else { + let lo3 = lo + lo; + let mid3 = mid + mid + (lo3 < lo) as u64; + let hi3 = hi + hi + (mid3 < mid) as u64; + let lo4 = lo3 - mul.0; + let mid4 = mid3 - mul.1 - (lo4 > lo3) as u64; + let hi4 = hi3 - (mid4 > mid3) as u64; + *vm = shiftright128(mid4, hi4, j - 64); + } + + shiftright128(mid, hi, j - 64 - 1) +} + #[cfg_attr(feature = "no-panic", inline)] pub(crate) fn decimal_length(v: u64) -> u32 { // This is slightly faster than a loop. diff --git a/src/lib.rs b/src/lib.rs index ab8927a..23171cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ mod d2s; mod d2s_full_table; mod digit_table; mod f2s; +#[cfg(not(integer128))] +mod mulshift128; mod pretty; pub use buffer::{Buffer, Float}; diff --git a/src/mulshift128.rs b/src/mulshift128.rs new file mode 100644 index 0000000..151bc78 --- /dev/null +++ b/src/mulshift128.rs @@ -0,0 +1,50 @@ +// Translated from C to Rust. The original C code can be found at +// https://github.com/ulfjack/ryu and carries the following license: +// +// Copyright 2018 Ulf Adams +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// (See accompanying file LICENSE-Apache or copy at +// http://www.apache.org/licenses/LICENSE-2.0) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. + +// Returns (lo, hi). +#[cfg_attr(feature = "no-panic", inline)] +pub fn umul128(a: u64, b: u64) -> (u64, u64) { + let a_lo = a as u32; + let a_hi = (a >> 32) as u32; + let b_lo = b as u32; + let b_hi = (b >> 32) as u32; + + let b00 = a_lo as u64 * b_lo as u64; + let b01 = a_lo as u64 * b_hi as u64; + let b10 = a_hi as u64 * b_lo as u64; + let b11 = a_hi as u64 * b_hi as u64; + + let mid_sum = b01 + b10; + let mid_carry = (mid_sum < b01) as u64; + + let product_lo = b00.wrapping_add(mid_sum << 32); + let product_lo_carry = (product_lo < b00) as u64; + + let product_hi = b11 + (mid_sum >> 32) + (mid_carry << 32) + product_lo_carry; + (product_lo, product_hi) +} + +#[cfg_attr(feature = "no-panic", inline)] +pub fn shiftright128(lo: u64, hi: u64, dist: u32) -> u64 { + // We don't need to handle the case dist >= 64 here (see above). + debug_assert!(dist > 0); + debug_assert!(dist < 64); + (hi << (64 - dist)) | (lo >> dist) +}