diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/ryu/src/s2d.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/ryu/src/s2d.rs')
-rw-r--r-- | third_party/rust/ryu/src/s2d.rs | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/third_party/rust/ryu/src/s2d.rs b/third_party/rust/ryu/src/s2d.rs new file mode 100644 index 0000000000..152ca97aba --- /dev/null +++ b/third_party/rust/ryu/src/s2d.rs @@ -0,0 +1,217 @@ +use crate::common::*; +use crate::d2s; +use crate::d2s_intrinsics::*; +use crate::parse::Error; +#[cfg(feature = "no-panic")] +use no_panic::no_panic; + +const DOUBLE_EXPONENT_BIAS: usize = 1023; + +fn floor_log2(value: u64) -> u32 { + 63_u32.wrapping_sub(value.leading_zeros()) +} + +#[cfg_attr(feature = "no-panic", no_panic)] +pub fn s2d(buffer: &[u8]) -> Result<f64, Error> { + let len = buffer.len(); + if len == 0 { + return Err(Error::InputTooShort); + } + + let mut m10digits = 0; + let mut e10digits = 0; + let mut dot_index = len; + let mut e_index = len; + let mut m10 = 0u64; + let mut e10 = 0i32; + let mut signed_m = false; + let mut signed_e = false; + + let mut i = 0; + if unsafe { *buffer.get_unchecked(0) } == b'-' { + signed_m = true; + i += 1; + } + + while let Some(c) = buffer.get(i).copied() { + if c == b'.' { + if dot_index != len { + return Err(Error::MalformedInput); + } + dot_index = i; + i += 1; + continue; + } + if c < b'0' || c > b'9' { + break; + } + if m10digits >= 17 { + return Err(Error::InputTooLong); + } + m10 = 10 * m10 + (c - b'0') as u64; + if m10 != 0 { + m10digits += 1; + } + i += 1; + } + + if let Some(b'e') | Some(b'E') = buffer.get(i) { + e_index = i; + i += 1; + match buffer.get(i) { + Some(b'-') => { + signed_e = true; + i += 1; + } + Some(b'+') => i += 1, + _ => {} + } + while let Some(c) = buffer.get(i).copied() { + if c < b'0' || c > b'9' { + return Err(Error::MalformedInput); + } + if e10digits > 3 { + // TODO: Be more lenient. Return +/-Infinity or +/-0 instead. + return Err(Error::InputTooLong); + } + e10 = 10 * e10 + (c - b'0') as i32; + if e10 != 0 { + e10digits += 1; + } + i += 1; + } + } + + if i < len { + return Err(Error::MalformedInput); + } + if signed_e { + e10 = -e10; + } + e10 -= if dot_index < e_index { + (e_index - dot_index - 1) as i32 + } else { + 0 + }; + if m10 == 0 { + return Ok(if signed_m { -0.0 } else { 0.0 }); + } + + if m10digits + e10 <= -324 || m10 == 0 { + // Number is less than 1e-324, which should be rounded down to 0; return + // +/-0.0. + let ieee = (signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS); + return Ok(f64::from_bits(ieee)); + } + if m10digits + e10 >= 310 { + // Number is larger than 1e+309, which should be rounded to +/-Infinity. + let ieee = ((signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS)) + | (0x7ff_u64 << d2s::DOUBLE_MANTISSA_BITS); + return Ok(f64::from_bits(ieee)); + } + + // Convert to binary float m2 * 2^e2, while retaining information about + // whether the conversion was exact (trailing_zeros). + let e2: i32; + let m2: u64; + let mut trailing_zeros: bool; + if e10 >= 0 { + // The length of m * 10^e in bits is: + // log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + e10 * log2(5) + // + // We want to compute the DOUBLE_MANTISSA_BITS + 1 top-most bits (+1 for + // the implicit leading one in IEEE format). We therefore choose a + // binary output exponent of + // log2(m10 * 10^e10) - (DOUBLE_MANTISSA_BITS + 1). + // + // We use floor(log2(5^e10)) so that we get at least this many bits; + // better to have an additional bit than to not have enough bits. + e2 = floor_log2(m10) + .wrapping_add(e10 as u32) + .wrapping_add(log2_pow5(e10) as u32) + .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS + 1) as i32; + + // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)]. + // To that end, we use the DOUBLE_POW5_SPLIT table. + let j = e2 + .wrapping_sub(e10) + .wrapping_sub(ceil_log2_pow5(e10)) + .wrapping_add(d2s::DOUBLE_POW5_BITCOUNT); + debug_assert!(j >= 0); + debug_assert!(e10 < d2s::DOUBLE_POW5_SPLIT.len() as i32); + m2 = mul_shift_64( + m10, + unsafe { d2s::DOUBLE_POW5_SPLIT.get_unchecked(e10 as usize) }, + j as u32, + ); + + // We also compute if the result is exact, i.e., + // [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2. + // This can only be the case if 2^e2 divides m10 * 10^e10, which in turn + // requires that the largest power of 2 that divides m10 + e10 is + // greater than e2. If e2 is less than e10, then the result must be + // exact. Otherwise we use the existing multiple_of_power_of_2 function. + trailing_zeros = + e2 < e10 || e2 - e10 < 64 && multiple_of_power_of_2(m10, (e2 - e10) as u32); + } else { + e2 = floor_log2(m10) + .wrapping_add(e10 as u32) + .wrapping_sub(ceil_log2_pow5(-e10) as u32) + .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS + 1) as i32; + let j = e2 + .wrapping_sub(e10) + .wrapping_add(ceil_log2_pow5(-e10)) + .wrapping_sub(1) + .wrapping_add(d2s::DOUBLE_POW5_INV_BITCOUNT); + debug_assert!(-e10 < d2s::DOUBLE_POW5_INV_SPLIT.len() as i32); + m2 = mul_shift_64( + m10, + unsafe { d2s::DOUBLE_POW5_INV_SPLIT.get_unchecked(-e10 as usize) }, + j as u32, + ); + trailing_zeros = multiple_of_power_of_5(m10, -e10 as u32); + } + + // Compute the final IEEE exponent. + let mut ieee_e2 = i32::max(0, e2 + DOUBLE_EXPONENT_BIAS as i32 + floor_log2(m2) as i32) as u32; + + if ieee_e2 > 0x7fe { + // Final IEEE exponent is larger than the maximum representable; return +/-Infinity. + let ieee = ((signed_m as u64) << (d2s::DOUBLE_EXPONENT_BITS + d2s::DOUBLE_MANTISSA_BITS)) + | (0x7ff_u64 << d2s::DOUBLE_MANTISSA_BITS); + return Ok(f64::from_bits(ieee)); + } + + // We need to figure out how much we need to shift m2. The tricky part is + // that we need to take the final IEEE exponent into account, so we need to + // reverse the bias and also special-case the value 0. + let shift = if ieee_e2 == 0 { 1 } else { ieee_e2 as i32 } + .wrapping_sub(e2) + .wrapping_sub(DOUBLE_EXPONENT_BIAS as i32) + .wrapping_sub(d2s::DOUBLE_MANTISSA_BITS as i32); + debug_assert!(shift >= 0); + + // We need to round up if the exact value is more than 0.5 above the value + // we computed. That's equivalent to checking if the last removed bit was 1 + // and either the value was not just trailing zeros or the result would + // otherwise be odd. + // + // We need to update trailing_zeros given that we have the exact output + // exponent ieee_e2 now. + trailing_zeros &= (m2 & ((1_u64 << (shift - 1)) - 1)) == 0; + let last_removed_bit = (m2 >> (shift - 1)) & 1; + let round_up = last_removed_bit != 0 && (!trailing_zeros || ((m2 >> shift) & 1) != 0); + + let mut ieee_m2 = (m2 >> shift).wrapping_add(round_up as u64); + debug_assert!(ieee_m2 <= 1_u64 << (d2s::DOUBLE_MANTISSA_BITS + 1)); + ieee_m2 &= (1_u64 << d2s::DOUBLE_MANTISSA_BITS) - 1; + if ieee_m2 == 0 && round_up { + // Due to how the IEEE represents +/-Infinity, we don't need to check + // for overflow here. + ieee_e2 += 1; + } + let ieee = ((((signed_m as u64) << d2s::DOUBLE_EXPONENT_BITS) | ieee_e2 as u64) + << d2s::DOUBLE_MANTISSA_BITS) + | ieee_m2; + Ok(f64::from_bits(ieee)) +} |