#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use crate::float::{ExtendedFloat80, RawFloat};
use crate::options::{Options, RoundMode};
use crate::shared;
use crate::table::*;
#[cfg(feature = "f16")]
use lexical_util::bf16::bf16;
#[cfg(feature = "f16")]
use lexical_util::f16::f16;
use lexical_util::format::{NumberFormat, STANDARD};
use lexical_util::num::{AsPrimitive, Float};
use lexical_write_integer::decimal::DigitCount;
use lexical_write_integer::write::WriteInteger;
#[inline]
pub unsafe fn write_float<F: RawFloat, const FORMAT: u128>(
float: F,
bytes: &mut [u8],
options: &Options,
) -> usize {
debug_assert!(!float.is_special());
debug_assert!(float >= F::ZERO);
let fp = to_decimal(float);
let digit_count = F::digit_count(fp.mant);
let sci_exp = fp.exp + digit_count as i32 - 1;
write_float!(
FORMAT,
sci_exp,
options,
write_float_scientific,
write_float_positive_exponent,
write_float_negative_exponent,
generic => F,
args => bytes, fp, sci_exp, options,
)
}
pub unsafe fn write_float_scientific<F: DragonboxFloat, const FORMAT: u128>(
bytes: &mut [u8],
fp: ExtendedFloat80,
sci_exp: i32,
options: &Options,
) -> usize {
debug_assert_eq!(count_factors(10, fp.mant), 0);
let format = NumberFormat::<{ FORMAT }> {};
assert!(format.is_valid());
let decimal_point = options.decimal_point();
let digits = unsafe { &mut index_unchecked_mut!(bytes[1..]) };
let digit_count = unsafe { F::write_digits(digits, fp.mant) };
let (digit_count, carried) =
unsafe { shared::truncate_and_round_decimal(digits, digit_count, options) };
let sci_exp = sci_exp + carried as i32;
let exact_count = shared::min_exact_digits(digit_count, options);
let mut cursor: usize;
unsafe {
index_unchecked_mut!(bytes[0] = bytes[1]);
index_unchecked_mut!(bytes[1]) = decimal_point;
if !format.no_exponent_without_fraction() && digit_count == 1 && options.trim_floats() {
cursor = 1;
} else if digit_count < exact_count {
cursor = digit_count + 1;
let zeros = exact_count - digit_count;
unsafe {
slice_fill_unchecked!(index_unchecked_mut!(bytes[cursor..cursor + zeros]), b'0');
}
cursor += zeros;
} else if digit_count == 1 {
index_unchecked_mut!(bytes[2]) = b'0';
cursor = 3;
} else {
cursor = digit_count + 1;
}
}
unsafe { shared::write_exponent::<FORMAT>(bytes, &mut cursor, sci_exp, options.exponent()) };
cursor
}
pub unsafe fn write_float_negative_exponent<F: DragonboxFloat, const FORMAT: u128>(
bytes: &mut [u8],
fp: ExtendedFloat80,
sci_exp: i32,
options: &Options,
) -> usize {
debug_assert!(sci_exp < 0);
debug_assert_eq!(count_factors(10, fp.mant), 0);
let decimal_point = options.decimal_point();
let sci_exp = sci_exp.wrapping_neg() as usize;
let mut cursor = sci_exp + 1;
debug_assert!(cursor >= 2);
unsafe {
let digits = &mut index_unchecked_mut!(bytes[..cursor]);
slice_fill_unchecked!(digits, b'0');
}
let digits = unsafe { &mut index_unchecked_mut!(bytes[cursor..]) };
let digit_count = unsafe { F::write_digits(digits, fp.mant) };
debug_assert!(cursor > 0);
let (digit_count, carried) =
unsafe { shared::truncate_and_round_decimal(digits, digit_count, options) };
let mut trimmed = false;
if carried && cursor == 2 {
unsafe {
index_unchecked_mut!(bytes[0]) = b'1';
if options.trim_floats() {
cursor = 1;
trimmed = true;
} else {
index_unchecked_mut!(bytes[1]) = decimal_point;
index_unchecked_mut!(bytes[2]) = b'0';
cursor = 3;
}
}
} else if carried {
unsafe {
index_unchecked_mut!(bytes[1]) = decimal_point;
index_unchecked_mut!(bytes[cursor - 1] = bytes[cursor]);
}
} else {
unsafe { index_unchecked_mut!(bytes[1]) = decimal_point };
cursor += digit_count;
}
let exact_count = shared::min_exact_digits(digit_count, options);
if !trimmed && digit_count < exact_count {
let zeros = exact_count - digit_count;
unsafe {
slice_fill_unchecked!(index_unchecked_mut!(bytes[cursor..cursor + zeros]), b'0');
}
cursor += zeros;
}
cursor
}
pub unsafe fn write_float_positive_exponent<F: DragonboxFloat, const FORMAT: u128>(
bytes: &mut [u8],
fp: ExtendedFloat80,
sci_exp: i32,
options: &Options,
) -> usize {
debug_assert!(sci_exp >= 0);
debug_assert_eq!(count_factors(10, fp.mant), 0);
let decimal_point = options.decimal_point();
let digit_count = unsafe { F::write_digits(bytes, fp.mant) };
let (mut digit_count, carried) =
unsafe { shared::truncate_and_round_decimal(bytes, digit_count, options) };
let leading_digits = sci_exp as usize + 1 + carried as usize;
let mut cursor: usize;
let mut trimmed = false;
if leading_digits >= digit_count {
unsafe {
let digits = &mut index_unchecked_mut!(bytes[digit_count..leading_digits]);
slice_fill_unchecked!(digits, b'0');
}
cursor = leading_digits;
digit_count = leading_digits;
if !options.trim_floats() {
unsafe { index_unchecked_mut!(bytes[cursor]) = decimal_point };
cursor += 1;
unsafe { index_unchecked_mut!(bytes[cursor]) = b'0' };
cursor += 1;
digit_count += 1;
} else {
trimmed = true;
}
} else {
let count = digit_count - leading_digits;
unsafe {
let buf = &mut index_unchecked_mut!(bytes[leading_digits..digit_count + 1]);
safe_assert!(buf.len() > count);
for i in (0..count).rev() {
index_unchecked_mut!(buf[i + 1] = buf[i]);
}
}
unsafe { index_unchecked_mut!(bytes[leading_digits]) = decimal_point };
cursor = digit_count + 1;
}
let exact_count = shared::min_exact_digits(digit_count, options);
if !trimmed && exact_count > digit_count {
let zeros = exact_count - digit_count;
unsafe {
let digits = &mut index_unchecked_mut!(bytes[cursor..cursor + zeros]);
slice_fill_unchecked!(digits, b'0');
}
cursor += zeros;
}
cursor
}
#[inline]
pub fn to_decimal<F: RawFloat>(float: F) -> ExtendedFloat80 {
let bits = float.to_bits();
let mantissa_bits = bits & F::MANTISSA_MASK;
if (bits & !F::SIGN_MASK).as_u64() == 0 {
return extended_float(0, 0);
}
if mantissa_bits.as_u64() == 0 {
compute_round_short(float)
} else {
compute_round(float)
}
}
#[inline(always)]
pub fn compute_round_short<F: RawFloat>(float: F) -> ExtendedFloat80 {
compute_nearest_shorter(float)
}
#[inline(always)]
pub fn compute_round<F: RawFloat>(float: F) -> ExtendedFloat80 {
compute_nearest_normal(float)
}
pub fn compute_nearest_shorter<F: RawFloat>(float: F) -> ExtendedFloat80 {
let exponent = float.exponent();
let minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
let beta = exponent + floor_log2_pow10(-minus_k);
let pow5 = unsafe { F::dragonbox_power(-minus_k) };
let mut xi = F::compute_left_endpoint(&pow5, beta);
let mut zi = F::compute_right_endpoint(&pow5, beta);
let interval_type = IntervalType::Closed;
if !interval_type.include_right_endpoint() && is_right_endpoint::<F>(exponent) {
zi -= 1;
}
if !(interval_type.include_left_endpoint() && is_left_endpoint::<F>(exponent)) {
xi += 1;
}
let significand = zi / 10;
if significand * 10 >= xi {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + 1);
return extended_float(mant, exp);
}
let mut significand = F::compute_round_up(&pow5, beta);
let bits: i32 = F::MANTISSA_SIZE;
let lower_threshold: i32 = -floor_log5_pow2_minus_log5_3(bits + 4) - 2 - bits;
let upper_threshold: i32 = -floor_log5_pow2(bits + 2) - 2 - bits;
let round_down = RoundMode::Round.prefer_round_down(significand);
if round_down && exponent >= lower_threshold && exponent <= upper_threshold {
significand -= 1;
} else if significand < xi {
significand += 1;
}
debug_assert!(float.exponent() == exponent);
debug_assert!(minus_k == floor_log10_pow2_minus_log10_4_over_3(exponent));
extended_float(significand, minus_k)
}
#[allow(clippy::comparison_chain)]
pub fn compute_nearest_normal<F: RawFloat>(float: F) -> ExtendedFloat80 {
let mantissa = float.mantissa().as_u64();
let exponent = float.exponent();
let is_even = mantissa % 2 == 0;
let minus_k = floor_log10_pow2(exponent) - F::KAPPA as i32;
let pow5 = unsafe { F::dragonbox_power(-minus_k) };
let beta = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta);
let (zi, is_z_integer) = F::compute_mul((two_fc | 1) << beta, &pow5);
let big_divisor = pow32(10, F::KAPPA + 1);
let small_divisor = pow32(10, F::KAPPA);
let exp = F::KAPPA + 1;
let n_max = (1 << (F::MANTISSA_SIZE + 1)) * big_divisor as u64 - 1;
let mut significand = F::divide_by_pow10(zi, exp, n_max);
let mut r = (zi - (big_divisor as u64).wrapping_mul(significand)) as u32;
let interval_type = IntervalType::Symmetric(is_even);
let mut should_short_circuit = true;
if r < deltai {
let include_right = interval_type.include_right_endpoint();
if r == 0 && !include_right && is_z_integer {
significand -= 1;
r = big_divisor;
should_short_circuit = false;
}
} else if r > deltai {
should_short_circuit = false;
} else {
let two_fl = two_fc - 1;
let include_left = interval_type.include_left_endpoint();
if !include_left || exponent < F::FC_PM_HALF_LOWER || exponent > F::DIV_BY_5_THRESHOLD {
let parity = F::compute_mul_parity(two_fl, &pow5, beta).0;
if !parity {
should_short_circuit = false;
}
} else {
let (xi_parity, x_is_integer) = F::compute_mul_parity(two_fl, &pow5, beta);
if !xi_parity && !x_is_integer {
should_short_circuit = false
}
}
}
if should_short_circuit {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
} else {
significand *= 10;
let dist = r - (deltai / 2) + (small_divisor / 2);
let approx_y_parity = ((dist ^ (small_divisor / 2)) & 1) != 0;
let (dist, is_dist_div_by_kappa) = F::check_div_pow10(dist);
significand += dist as u64;
if is_dist_div_by_kappa {
let (yi_parity, is_y_integer) = F::compute_mul_parity(two_fc, &pow5, beta);
let round_down = RoundMode::Round.prefer_round_down(significand);
if yi_parity != approx_y_parity || (is_y_integer && round_down) {
significand -= 1;
}
}
debug_assert!(float.exponent() == exponent);
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
extended_float(significand, minus_k + F::KAPPA as i32)
}
}
#[allow(clippy::comparison_chain)]
pub fn compute_left_closed_directed<F: RawFloat>(float: F) -> ExtendedFloat80 {
let mantissa = float.mantissa().as_u64();
let exponent = float.exponent();
let minus_k = floor_log10_pow2(exponent) - F::KAPPA as i32;
let pow5 = unsafe { F::dragonbox_power(-minus_k) };
let beta = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta);
let (mut xi, mut is_x_integer) = F::compute_mul(two_fc << beta, &pow5);
if F::BITS == 32 && exponent <= -80 {
is_x_integer = false;
}
if !is_x_integer {
xi += 1;
}
let big_divisor = pow32(10, F::KAPPA + 1);
let exp = F::KAPPA + 1;
let n_max = (1 << (F::MANTISSA_SIZE + 1)) * big_divisor as u64 - 1;
let mut significand = F::divide_by_pow10(xi, exp, n_max);
let mut r = (xi - (big_divisor as u64).wrapping_mul(significand)) as u32;
if r != 0 {
significand += 1;
r = big_divisor - r;
}
let mut should_short_circuit = true;
if r > deltai {
should_short_circuit = false;
} else if r == deltai {
let (zi_parity, is_z_integer) = F::compute_mul_parity(two_fc + 2, &pow5, beta);
if zi_parity || is_z_integer {
should_short_circuit = false;
}
}
if should_short_circuit {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
} else {
significand *= 10;
significand -= F::div_pow10(r) as u64;
debug_assert!(float.exponent() == exponent);
debug_assert!(minus_k == floor_log10_pow2(exponent) - F::KAPPA as i32);
extended_float(significand, minus_k + F::KAPPA as i32)
}
}
#[allow(clippy::comparison_chain, clippy::if_same_then_else)]
pub fn compute_right_closed_directed<F: RawFloat>(float: F, shorter: bool) -> ExtendedFloat80 {
let mantissa = float.mantissa().as_u64();
let exponent = float.exponent();
let minus_k = floor_log10_pow2(exponent - shorter as i32) - F::KAPPA as i32;
let pow5 = unsafe { F::dragonbox_power(-minus_k) };
let beta = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta - shorter as i32);
let zi = F::compute_mul(two_fc << beta, &pow5).0;
let big_divisor = pow32(10, F::KAPPA + 1);
let exp = F::KAPPA + 1;
let n_max = (1 << (F::MANTISSA_SIZE + 1)) * big_divisor as u64 - 1;
let mut significand = F::divide_by_pow10(zi, exp, n_max);
let r = (zi - (big_divisor as u64).wrapping_mul(significand)) as u32;
let mut should_short_circuit = true;
if r > deltai {
should_short_circuit = false;
} else if r == deltai {
let two_f = two_fc
- if shorter {
1
} else {
2
};
if !F::compute_mul_parity(two_f, &pow5, beta).0 {
should_short_circuit = false;
}
}
if should_short_circuit {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
} else {
significand *= 10;
significand -= F::div_pow10(r) as u64;
debug_assert!(float.exponent() == exponent);
debug_assert!(minus_k == floor_log10_pow2(exponent - shorter as i32) - F::KAPPA as i32);
extended_float(significand, minus_k + F::KAPPA as i32)
}
}
#[inline]
pub unsafe fn write_digits_u32(bytes: &mut [u8], mantissa: u32) -> usize {
debug_assert!(bytes.len() >= 10);
unsafe { mantissa.write_mantissa::<u32, { STANDARD }>(bytes) }
}
#[inline]
#[allow(clippy::branches_sharing_code)]
pub unsafe fn write_digits_u64(bytes: &mut [u8], mantissa: u64) -> usize {
debug_assert!(bytes.len() >= 20);
unsafe { mantissa.write_mantissa::<u64, { STANDARD }>(bytes) }
}
#[inline(always)]
pub const fn extended_float(mant: u64, exp: i32) -> ExtendedFloat80 {
ExtendedFloat80 {
mant,
exp,
}
}
#[inline(always)]
pub const fn floor_log2(mut n: u64) -> i32 {
let mut count = -1;
while n != 0 {
count += 1;
n >>= 1;
}
count
}
#[inline(always)]
pub const fn is_endpoint(exponent: i32, lower: i32, upper: i32) -> bool {
exponent >= lower && exponent <= upper
}
#[inline(always)]
pub fn is_right_endpoint<F: Float>(exponent: i32) -> bool {
let lower_threshold = 0;
let factors = count_factors(5, (1u64 << (F::MANTISSA_SIZE + 1)) + 1) + 1;
let upper_threshold = 2 + floor_log2(pow64(10, factors) / 3);
is_endpoint(exponent, lower_threshold, upper_threshold)
}
#[inline(always)]
pub fn is_left_endpoint<F: Float>(exponent: i32) -> bool {
let lower_threshold = 2;
let factors = count_factors(5, (1u64 << (F::MANTISSA_SIZE + 2)) - 1) + 1;
let upper_threshold = 2 + floor_log2(pow64(10, factors) / 3);
is_endpoint(exponent, lower_threshold, upper_threshold)
}
#[inline(always)]
pub const fn umul128_upper64(x: u64, y: u64) -> u64 {
let p = x as u128 * y as u128;
(p >> 64) as u64
}
#[inline(always)]
pub const fn umul192_upper128(x: u64, hi: u64, lo: u64) -> (u64, u64) {
let mut r = x as u128 * hi as u128;
r += umul128_upper64(x, lo) as u128;
((r >> 64) as u64, r as u64)
}
#[inline(always)]
pub const fn umul192_lower128(x: u64, yhi: u64, ylo: u64) -> (u64, u64) {
let hi = x.wrapping_mul(yhi);
let hi_lo = x as u128 * ylo as u128;
(hi.wrapping_add((hi_lo >> 64) as u64), hi_lo as u64)
}
#[inline(always)]
pub const fn umul96_upper64(x: u64, y: u64) -> u64 {
umul128_upper64(x << 32, y)
}
#[inline(always)]
pub const fn umul96_lower64(x: u64, y: u64) -> u64 {
x.wrapping_mul(y)
}
#[inline(always)]
pub const fn floor_log5_pow2(q: i32) -> i32 {
q.wrapping_mul(225799) >> 19
}
#[inline(always)]
pub const fn floor_log10_pow2(q: i32) -> i32 {
q.wrapping_mul(315653) >> 20
}
#[inline(always)]
pub const fn floor_log2_pow10(q: i32) -> i32 {
q.wrapping_mul(1741647) >> 19
}
#[inline(always)]
pub const fn floor_log5_pow2_minus_log5_3(q: i32) -> i32 {
q.wrapping_mul(451597).wrapping_sub(715764) >> 20
}
#[inline(always)]
pub const fn floor_log10_pow2_minus_log10_4_over_3(q: i32) -> i32 {
q.wrapping_mul(1262611).wrapping_sub(524031) >> 22
}
#[inline(always)]
pub const fn pow32(radix: u32, mut exp: u32) -> u32 {
let mut p = 1;
while exp > 0 {
p *= radix;
exp -= 1;
}
p
}
#[inline(always)]
pub const fn pow64(radix: u32, mut exp: u32) -> u64 {
let mut p = 1;
while exp > 0 {
p *= radix as u64;
exp -= 1;
}
p
}
#[inline(always)]
pub const fn count_factors(radix: u32, mut n: u64) -> u32 {
let mut c = 0;
while n != 0 && n % radix as u64 == 0 {
n /= radix as u64;
c += 1;
}
c
}
#[inline(always)]
pub const fn divide_by_pow10_32(n: u32, exp: u32) -> u32 {
if exp == 2 {
((n as u64 * 1374389535) >> 37) as u32
} else {
let divisor = pow32(exp as u32, 10);
n / divisor
}
}
#[inline(always)]
pub const fn divide_by_pow10_64(n: u64, exp: u32, n_max: u64) -> u64 {
if exp == 3 && n_max <= 15534100272597517998 {
umul128_upper64(n, 2361183241434822607) >> 7
} else {
let divisor = pow64(exp as u32, 10);
n / divisor
}
}
impl RoundMode {
#[inline(always)]
pub const fn prefer_round_down(&self, significand: u64) -> bool {
match self {
RoundMode::Round => significand % 2 != 0,
RoundMode::Truncate => true,
}
}
}
#[non_exhaustive]
pub enum IntervalType {
Symmetric(bool),
Asymmetric(bool),
Closed,
Open,
LeftClosedRightOpen,
RightClosedLeftOpen,
}
impl IntervalType {
#[inline(always)]
pub fn is_symmetric(&self) -> bool {
match self {
Self::Symmetric(_) => true,
Self::Asymmetric(_) => false,
Self::Closed => true,
Self::Open => true,
Self::LeftClosedRightOpen => false,
Self::RightClosedLeftOpen => false,
}
}
#[inline(always)]
pub fn include_left_endpoint(&self) -> bool {
match self {
Self::Symmetric(closed) => *closed,
Self::Asymmetric(left_closed) => *left_closed,
Self::Closed => true,
Self::Open => false,
Self::LeftClosedRightOpen => true,
Self::RightClosedLeftOpen => false,
}
}
#[inline(always)]
pub fn include_right_endpoint(&self) -> bool {
match self {
Self::Symmetric(closed) => *closed,
Self::Asymmetric(left_closed) => !*left_closed,
Self::Closed => true,
Self::Open => false,
Self::LeftClosedRightOpen => false,
Self::RightClosedLeftOpen => true,
}
}
}
#[inline(always)]
pub fn compute_left_endpoint_u64<F: DragonboxFloat>(pow5: u64, beta: i32) -> u64 {
let zero_carry = pow5 >> (F::MANTISSA_SIZE as usize + 2);
let mantissa_shift = 64 - F::MANTISSA_SIZE as usize - 1;
(pow5 - zero_carry) >> (mantissa_shift as i32 - beta)
}
#[inline(always)]
pub fn compute_right_endpoint_u64<F: DragonboxFloat>(pow5: u64, beta: i32) -> u64 {
let zero_carry = pow5 >> (F::MANTISSA_SIZE as usize + 1);
let mantissa_shift = 64 - F::MANTISSA_SIZE as usize - 1;
(pow5 + zero_carry) >> (mantissa_shift as i32 - beta)
}
#[inline(always)]
pub fn compute_round_up_u64<F: DragonboxFloat>(pow5: u64, beta: i32) -> u64 {
let shift = 64 - F::MANTISSA_SIZE - 2;
((pow5 >> (shift - beta)) + 1) / 2
}
#[inline(always)]
pub const fn high(pow5: &(u64, u64)) -> u64 {
pow5.0
}
#[inline(always)]
pub const fn low(pow5: &(u64, u64)) -> u64 {
pow5.1
}
#[inline(always)]
pub const fn rotr32(n: u32, r: u32) -> u32 {
let r = r & 31;
(n >> r) | (n << (32 - r))
}
#[inline(always)]
pub const fn rotr64(n: u64, r: u64) -> u64 {
let r = r & 63;
(n >> r) | (n << (64 - r))
}
struct Div10Info {
magic_number: u32,
shift_amount: i32,
}
const F32_DIV10_INFO: Div10Info = Div10Info {
magic_number: 6554,
shift_amount: 16,
};
const F64_DIV10_INFO: Div10Info = Div10Info {
magic_number: 656,
shift_amount: 16,
};
macro_rules! check_div_pow10 {
($n:ident, $exp:literal, $float:ident, $info:ident) => {{
debug_assert!($exp + 2 < floor_log10_pow2(31));
debug_assert!($n as u64 <= pow64(10, $exp + 1));
let n = $n.wrapping_mul($info.magic_number);
let mask = (1u32 << $info.shift_amount) - 1;
let r = (n & mask) < $info.magic_number;
(n >> $info.shift_amount, r)
}};
}
const MOD_INV_5_U32: u32 = 0xCCCC_CCCD;
const MOD_INV_25_U32: u32 = MOD_INV_5_U32.wrapping_mul(MOD_INV_5_U32);
const MOD_INV_5_U64: u64 = 0xCCCC_CCCC_CCCC_CCCD;
const MOD_INV_25_U64: u64 = MOD_INV_5_U64.wrapping_mul(MOD_INV_5_U64);
macro_rules! div_pow10 {
($n:ident, $info:ident) => {{
$n.wrapping_mul($info.magic_number) >> $info.shift_amount
}};
}
pub trait DragonboxFloat: Float {
const KAPPA: u32;
const DECIMAL_DIGITS: usize;
const FC_PM_HALF_LOWER: i32 = -(Self::KAPPA as i32) - floor_log5_pow2(Self::KAPPA as i32);
const DIV_BY_5_THRESHOLD: i32 = floor_log2_pow10(Self::KAPPA as i32 + 1);
type Power;
fn digit_count(mantissa: u64) -> usize;
unsafe fn write_digits(bytes: &mut [u8], mantissa: u64) -> usize;
unsafe fn dragonbox_power(exponent: i32) -> Self::Power;
fn compute_left_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64;
fn compute_right_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64;
fn compute_round_up(pow5: &Self::Power, beta_minus_1: i32) -> u64;
fn compute_mul(u: u64, pow5: &Self::Power) -> (u64, bool);
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta_minus_1: i32) -> (bool, bool);
fn compute_delta(pow5: &Self::Power, beta_minus_1: i32) -> u32;
fn process_trailing_zeros(mantissa: u64, exponent: i32) -> (u64, i32);
fn remove_trailing_zeros(mantissa: u64) -> (u64, i32);
#[inline(always)]
fn divisible_by_pow2(x: u64, exp: u32) -> bool {
x.trailing_zeros() >= exp
}
fn check_div_pow10(n: u32) -> (u32, bool);
fn div_pow10(n: u32) -> u32;
fn divide_by_pow10(n: u64, exp: u32, n_max: u64) -> u64;
}
impl DragonboxFloat for f32 {
const KAPPA: u32 = 1;
const DECIMAL_DIGITS: usize = 9;
type Power = u64;
#[inline(always)]
fn digit_count(mantissa: u64) -> usize {
debug_assert!(mantissa <= u32::MAX as u64);
(mantissa as u32).digit_count()
}
#[inline(always)]
unsafe fn write_digits(bytes: &mut [u8], mantissa: u64) -> usize {
debug_assert!(mantissa <= u32::MAX as u64);
unsafe { write_digits_u32(bytes, mantissa as u32) }
}
#[inline(always)]
unsafe fn dragonbox_power(exponent: i32) -> Self::Power {
debug_assert!((SMALLEST_F32_POW5..=LARGEST_F32_POW5).contains(&exponent));
let index = (exponent - SMALLEST_F32_POW5) as usize;
unsafe { index_unchecked!(DRAGONBOX32_POWERS_OF_FIVE[index]) }
}
#[inline(always)]
fn compute_left_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_left_endpoint_u64::<Self>(*pow5, beta_minus_1)
}
#[inline(always)]
fn compute_right_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_right_endpoint_u64::<Self>(*pow5, beta_minus_1)
}
#[inline(always)]
fn compute_round_up(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_round_up_u64::<Self>(*pow5, beta_minus_1)
}
#[inline(always)]
fn compute_mul(u: u64, pow5: &Self::Power) -> (u64, bool) {
let r = umul96_upper64(u, *pow5);
(r >> 32, (r as u32) == 0)
}
#[inline(always)]
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta: i32) -> (bool, bool) {
debug_assert!((1..64).contains(&beta));
let r = umul96_lower64(two_f, *pow5);
let parity = (r >> (64 - beta)) & 1;
let is_integer = r >> (32 - beta);
(parity != 0, is_integer == 0)
}
#[inline(always)]
fn compute_delta(pow5: &Self::Power, beta: i32) -> u32 {
(*pow5 >> (64 - 1 - beta)) as u32
}
#[inline(always)]
fn process_trailing_zeros(mantissa: u64, exponent: i32) -> (u64, i32) {
let (mantissa, trailing) = Self::remove_trailing_zeros(mantissa);
(mantissa, exponent + trailing)
}
#[inline(always)]
fn remove_trailing_zeros(mantissa: u64) -> (u64, i32) {
debug_assert!(mantissa <= u32::MAX as u64);
debug_assert!(mantissa != 0);
let mut n = mantissa as u32;
let mut quo: u32;
let mut s: i32 = 0;
loop {
quo = rotr32(n.wrapping_mul(MOD_INV_25_U32), 2);
if quo <= u32::MAX / 100 {
n = quo;
s += 2;
} else {
break;
}
}
quo = rotr32(n.wrapping_mul(MOD_INV_5_U32), 1);
if quo <= u32::MAX / 10 {
n = quo;
s |= 1;
}
(n as u64, s)
}
#[inline(always)]
fn check_div_pow10(n: u32) -> (u32, bool) {
check_div_pow10!(n, 1, f32, F32_DIV10_INFO)
}
#[inline(always)]
fn div_pow10(n: u32) -> u32 {
div_pow10!(n, F32_DIV10_INFO)
}
#[inline(always)]
fn divide_by_pow10(n: u64, exp: u32, _: u64) -> u64 {
divide_by_pow10_32(n as u32, exp) as u64
}
}
impl DragonboxFloat for f64 {
const KAPPA: u32 = 2;
const DECIMAL_DIGITS: usize = 17;
type Power = (u64, u64);
#[inline(always)]
fn digit_count(mantissa: u64) -> usize {
mantissa.digit_count()
}
#[inline(always)]
unsafe fn write_digits(bytes: &mut [u8], mantissa: u64) -> usize {
unsafe { write_digits_u64(bytes, mantissa) }
}
#[inline(always)]
unsafe fn dragonbox_power(exponent: i32) -> Self::Power {
debug_assert!((SMALLEST_F64_POW5..=LARGEST_F64_POW5).contains(&exponent));
let index = (exponent - SMALLEST_F64_POW5) as usize;
unsafe { index_unchecked!(DRAGONBOX64_POWERS_OF_FIVE[index]) }
}
#[inline(always)]
fn compute_left_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_left_endpoint_u64::<Self>(high(pow5), beta_minus_1)
}
#[inline(always)]
fn compute_right_endpoint(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_right_endpoint_u64::<Self>(high(pow5), beta_minus_1)
}
#[inline(always)]
fn compute_round_up(pow5: &Self::Power, beta_minus_1: i32) -> u64 {
compute_round_up_u64::<Self>(high(pow5), beta_minus_1)
}
#[inline(always)]
fn compute_mul(u: u64, pow5: &Self::Power) -> (u64, bool) {
let (hi, lo) = umul192_upper128(u, high(pow5), low(pow5));
(hi, lo == 0)
}
#[inline(always)]
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta: i32) -> (bool, bool) {
debug_assert!((1..64).contains(&beta));
let (rhi, rlo) = umul192_lower128(two_f, high(pow5), low(pow5));
let parity = (rhi >> (64 - beta)) & 1;
let is_integer = (rhi << beta) | (rlo >> (64 - beta));
(parity != 0, is_integer == 0)
}
#[inline(always)]
fn compute_delta(pow5: &Self::Power, beta: i32) -> u32 {
(high(pow5) >> (64 - 1 - beta)) as u32
}
#[inline(always)]
fn process_trailing_zeros(mantissa: u64, exponent: i32) -> (u64, i32) {
let (mantissa, trailing) = Self::remove_trailing_zeros(mantissa);
(mantissa, exponent + trailing)
}
#[inline(always)]
fn remove_trailing_zeros(mantissa: u64) -> (u64, i32) {
debug_assert!(mantissa != 0);
let magic_number = 12379400392853802749u64;
let nm = mantissa as u128 * magic_number as u128;
let high = (nm >> 64) as u64;
let mask = (1 << (90 - 64)) - 1;
let low = nm as u64;
if high & mask == 0 && low < magic_number {
let mut n = (high >> (90 - 64)) as u32;
let mut s: i32 = 8;
let mut quo: u32;
loop {
quo = rotr32(n.wrapping_mul(MOD_INV_25_U32), 2);
if quo <= u32::MAX / 100 {
n = quo;
s += 2;
} else {
break;
}
}
quo = rotr32(n.wrapping_mul(MOD_INV_5_U32), 1);
if quo <= u32::MAX / 10 {
n = quo;
s |= 1;
}
(n as u64, s)
} else {
let mut n = mantissa;
let mut s: i32 = 0;
let mut quo: u64;
loop {
quo = rotr64(n.wrapping_mul(MOD_INV_25_U64), 2);
if quo <= u64::MAX / 100 {
n = quo;
s += 2;
} else {
break;
}
}
quo = rotr64(n.wrapping_mul(MOD_INV_5_U64), 1);
if quo <= u64::MAX / 10 {
n = quo;
s |= 1;
}
(n, s)
}
}
#[inline(always)]
fn check_div_pow10(n: u32) -> (u32, bool) {
check_div_pow10!(n, 2, f64, F64_DIV10_INFO)
}
#[inline(always)]
fn div_pow10(n: u32) -> u32 {
div_pow10!(n, F64_DIV10_INFO)
}
#[inline(always)]
fn divide_by_pow10(n: u64, exp: u32, n_max: u64) -> u64 {
divide_by_pow10_64(n, exp, n_max)
}
}
#[cfg(feature = "f16")]
macro_rules! dragonbox_unimpl {
($($t:ident)*) => ($(
impl DragonboxFloat for $t {
const KAPPA: u32 = 0;
const DECIMAL_DIGITS: usize = 0;
type Power = u64;
#[inline(always)]
fn digit_count(_: u64) -> usize {
unimplemented!()
}
#[inline(always)]
unsafe fn write_digits(_: &mut [u8], _: u64) -> usize {
unimplemented!()
}
#[inline(always)]
unsafe fn dragonbox_power(_: i32) -> Self::Power {
unimplemented!()
}
#[inline(always)]
fn compute_left_endpoint(_: &Self::Power, _: i32) -> u64 {
unimplemented!()
}
#[inline(always)]
fn compute_right_endpoint(_: &Self::Power, _: i32) -> u64 {
unimplemented!()
}
#[inline(always)]
fn compute_round_up(_: &Self::Power, _: i32) -> (u64, bool) {
unimplemented!()
}
#[inline(always)]
fn compute_mul(_: u64, _: &Self::Power) -> (u64, bool) {
unimplemented!()
}
#[inline(always)]
fn compute_mul_parity(_: u64, _: &Self::Power, _: i32) -> (bool, bool) {
unimplemented!()
}
#[inline(always)]
fn compute_delta(_: &Self::Power, _: i32) -> u32 {
unimplemented!()
}
#[inline(always)]
fn process_trailing_zeros(_: u64, _: i32) -> (u64, i32) {
unimplemented!()
}
#[inline(always)]
fn remove_trailing_zeros(_: u64) -> (u64, i32) {
unimplemented!()
}
#[inline(always)]
fn check_div_pow10(_: u32) -> (u32, bool) {
unimplemented!()
}
#[inline(always)]
fn div_pow10(_: u32) -> u32 {
unimplemented!()
}
}
)*);
}
#[cfg(feature = "f16")]
dragonbox_unimpl! { bf16 f16 }