summaryrefslogtreecommitdiffstats
path: root/third_party/rust/ryu/src/d2s.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/ryu/src/d2s.rs')
-rw-r--r--third_party/rust/ryu/src/d2s.rs300
1 files changed, 300 insertions, 0 deletions
diff --git a/third_party/rust/ryu/src/d2s.rs b/third_party/rust/ryu/src/d2s.rs
new file mode 100644
index 0000000000..392577a9a1
--- /dev/null
+++ b/third_party/rust/ryu/src/d2s.rs
@@ -0,0 +1,300 @@
+// 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.
+
+use crate::common::*;
+#[cfg(not(feature = "small"))]
+pub use crate::d2s_full_table::*;
+use crate::d2s_intrinsics::*;
+#[cfg(feature = "small")]
+pub use crate::d2s_small_table::*;
+use core::mem::MaybeUninit;
+
+pub const DOUBLE_MANTISSA_BITS: u32 = 52;
+pub const DOUBLE_EXPONENT_BITS: u32 = 11;
+pub const DOUBLE_BIAS: i32 = 1023;
+pub const DOUBLE_POW5_INV_BITCOUNT: i32 = 125;
+pub const DOUBLE_POW5_BITCOUNT: i32 = 125;
+
+#[cfg_attr(feature = "no-panic", inline)]
+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.
+ // (17 digits are sufficient for round-tripping.)
+ debug_assert!(v < 100000000000000000);
+
+ if v >= 10000000000000000 {
+ 17
+ } else if v >= 1000000000000000 {
+ 16
+ } else if v >= 100000000000000 {
+ 15
+ } else if v >= 10000000000000 {
+ 14
+ } else if v >= 1000000000000 {
+ 13
+ } else if v >= 100000000000 {
+ 12
+ } else if v >= 10000000000 {
+ 11
+ } else if v >= 1000000000 {
+ 10
+ } else 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 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 (e2, m2) = if ieee_exponent == 0 {
+ (
+ // We subtract 2 so that the bounds computation has 2 additional bits.
+ 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
+ ieee_mantissa,
+ )
+ } else {
+ (
+ 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 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;
+ // We would compute mp and mm like this:
+ // uint64_t mp = 4 * m2 + 2;
+ // uint64_t mm = mv - 1 - mm_shift;
+
+ // Step 3: Convert to a decimal power base using 128-bit arithmetic.
+ let mut vr: u64;
+ let mut vp: u64;
+ let mut vm: u64;
+ let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
+ let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
+ let e10: i32;
+ let mut vm_is_trailing_zeros = false;
+ let mut vr_is_trailing_zeros = false;
+ 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 u32;
+ e10 = q as i32;
+ let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
+ let i = -e2 + q as i32 + k;
+ vr = unsafe {
+ mul_shift_all_64(
+ m2,
+ #[cfg(feature = "small")]
+ &compute_inv_pow5(q),
+ #[cfg(not(feature = "small"))]
+ {
+ debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
+ DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
+ },
+ i as u32,
+ vp_uninit.as_mut_ptr(),
+ vm_uninit.as_mut_ptr(),
+ mm_shift,
+ )
+ };
+ vp = unsafe { vp_uninit.assume_init() };
+ vm = unsafe { vm_uninit.assume_init() };
+ if q <= 21 {
+ // 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 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 {
+ // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
+ // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
+ // <=> true && pow5_factor(mm) >= q, since e2 >= q.
+ vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
+ } else {
+ // Same as min(e2 + 1, pow5_factor(mp)) >= q.
+ vp -= multiple_of_power_of_5(mv + 2, q) as u64;
+ }
+ }
+ } else {
+ // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
+ let q = log10_pow5(-e2) - (-e2 > 1) as u32;
+ e10 = q as i32 + e2;
+ let i = -e2 - q as i32;
+ let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
+ let j = q as i32 - k;
+ vr = unsafe {
+ mul_shift_all_64(
+ m2,
+ #[cfg(feature = "small")]
+ &compute_pow5(i as u32),
+ #[cfg(not(feature = "small"))]
+ {
+ debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
+ DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
+ },
+ j as u32,
+ vp_uninit.as_mut_ptr(),
+ vm_uninit.as_mut_ptr(),
+ mm_shift,
+ )
+ };
+ vp = unsafe { vp_uninit.assume_init() };
+ vm = unsafe { vm_uninit.assume_init() };
+ if q <= 1 {
+ // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
+ // mv = 4 * m2, so it always has at least two trailing 0 bits.
+ vr_is_trailing_zeros = true;
+ if accept_bounds {
+ // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
+ vm_is_trailing_zeros = mm_shift == 1;
+ } else {
+ // mp = mv + 2, so it always has at least one trailing 0 bit.
+ vp -= 1;
+ }
+ } else if q < 63 {
+ // TODO(ulfjack): Use a tighter bound here.
+ // 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 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 {
+ // General case, which happens rarely (~0.7%).
+ loop {
+ let vp_div10 = div10(vp);
+ let vm_div10 = div10(vm);
+ if vp_div10 <= vm_div10 {
+ break;
+ }
+ let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
+ let vr_div10 = div10(vr);
+ 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;
+ vr = vr_div10;
+ vp = vp_div10;
+ vm = vm_div10;
+ removed += 1;
+ }
+ if vm_is_trailing_zeros {
+ loop {
+ let vm_div10 = div10(vm);
+ 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 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;
+ vp = vp_div10;
+ vm = vm_div10;
+ removed += 1;
+ }
+ }
+ if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
+ // Round even if the exact number is .....50..0.
+ last_removed_digit = 4;
+ }
+ // We need to take vr + 1 if vr is outside bounds or we need to round up.
+ vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
+ as u64
+ } else {
+ // Specialized for the common case (~99.3%). Percentages below are relative to this.
+ let mut round_up = false;
+ let vp_div100 = div100(vp);
+ let vm_div100 = div100(vm);
+ // Optimization: remove two digits at a time (~86.2%).
+ if vp_div100 > vm_div100 {
+ let vr_div100 = div100(vr);
+ 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;
+ vm = vm_div100;
+ removed += 2;
+ }
+ // Loop iterations below (approximately), without optimization above:
+ // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
+ // Loop iterations below (approximately), with optimization above:
+ // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
+ loop {
+ let vp_div10 = div10(vp);
+ let vm_div10 = div10(vm);
+ if vp_div10 <= vm_div10 {
+ break;
+ }
+ let vr_div10 = div10(vr);
+ 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;
+ vm = vm_div10;
+ removed += 1;
+ }
+ // 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;
+
+ FloatingDecimal64 {
+ exponent: exp,
+ mantissa: output,
+ }
+}