#![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, Integer};
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 mut 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(&mut 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 src = index_unchecked!(bytes[leading_digits..digit_count]).as_ptr();
let dst = &mut index_unchecked_mut!(bytes[leading_digits + 1..digit_count + 1]);
copy_unchecked!(dst, src, count);
}
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_minus_1 = exponent + floor_log2_pow10(-minus_k);
let pow5 = unsafe { F::dragonbox_power(-minus_k) };
let mut xi = F::compute_left_endpoint(&pow5, beta_minus_1);
let mut zi = F::compute_right_endpoint(&pow5, beta_minus_1);
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_minus_1);
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;
if exponent >= lower_threshold && exponent <= upper_threshold {
significand = RoundMode::Round.break_rounding_tie(significand);
} 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_minus_1 = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta_minus_1);
let two_fr = two_fc | 1;
let zi = F::compute_mul(two_fr << beta_minus_1, &pow5);
let big_divisor = pow32(10, F::KAPPA + 1);
let small_divisor = pow32(10, F::KAPPA);
let exp = F::KAPPA + 1;
let max_pow2 = F::MANTISSA_SIZE + F::KAPPA as i32 + 2;
let max_pow5 = F::KAPPA as i32 + 1;
let mut significand = divide_by_pow10(zi, exp, max_pow2, max_pow5);
let mut r = (zi - big_divisor as u64 * significand) as u32;
let interval_type = IntervalType::Symmetric(is_even);
let short_circuit = || {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
};
if r < deltai {
let include_right = interval_type.include_right_endpoint();
if r == 0 && !include_right && F::is_product_fc_pm_half(two_fr, exponent, minus_k) {
significand -= 1;
r = big_divisor;
} else {
return short_circuit();
}
} else if r == deltai {
let two_fl = two_fc - 1;
let include_left = interval_type.include_left_endpoint();
let is_prod = F::is_product_fc_pm_half(two_fl, exponent, minus_k);
let is_mul_parity = F::compute_mul_parity(two_fl, &pow5, beta_minus_1);
if (include_left && is_prod) || is_mul_parity {
return short_circuit();
}
}
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 {
if F::compute_mul_parity(two_fc, &pow5, beta_minus_1) != approx_y_parity {
significand -= 1;
} else {
if F::is_product_fc(two_fc, exponent, minus_k) {
significand = RoundMode::Round.break_rounding_tie(significand);
}
}
}
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_minus_1 = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta_minus_1);
let mut xi = F::compute_mul(two_fc << beta_minus_1, &pow5);
if !F::is_product_fc(two_fc, exponent, minus_k) {
xi += 1;
}
let big_divisor = pow32(10, F::KAPPA + 1);
let exp = F::KAPPA + 1;
let max_pow2 = F::MANTISSA_SIZE + F::KAPPA as i32 + 2;
let max_pow5 = F::KAPPA as i32 + 1;
let mut significand = divide_by_pow10(xi, exp, max_pow2, max_pow5);
let mut r = (xi - big_divisor as u64 * significand) as u32;
if r != 0 {
significand += 1;
r = big_divisor - r;
}
let short_circuit = || {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
};
if r < deltai {
return short_circuit();
} else if r == deltai {
let is_prod = F::is_product_fc(two_fc + 2, exponent, minus_k);
let is_mul_parity = F::compute_mul_parity(two_fc + 2, &pow5, beta_minus_1);
if !(is_mul_parity || is_prod) {
return short_circuit();
}
}
significand *= 10;
significand -= F::small_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_minus_1 = exponent + floor_log2_pow10(-minus_k);
let two_fc = mantissa << 1;
let deltai = F::compute_delta(&pow5, beta_minus_1 - shorter as i32);
let zi = F::compute_mul(two_fc << beta_minus_1, &pow5);
let big_divisor = pow32(10, F::KAPPA + 1);
let exp = F::KAPPA + 1;
let max_pow2 = F::MANTISSA_SIZE + F::KAPPA as i32 + 2;
let max_pow5 = F::KAPPA as i32 + 1;
let mut significand = divide_by_pow10(zi, exp, max_pow2, max_pow5);
let r = (zi - big_divisor as u64 * significand) as u32;
let short_circuit = || {
let (mant, exp) = F::process_trailing_zeros(significand, minus_k + F::KAPPA as i32 + 1);
extended_float(mant, exp)
};
if r < deltai {
return short_circuit();
} else if r == deltai {
if shorter && F::compute_mul_parity(two_fc * 2 - 1, &pow5, beta_minus_1 - 1) {
return short_circuit();
} else if !shorter && F::compute_mul_parity(two_fc - 1, &pow5, beta_minus_1) {
return short_circuit();
}
}
significand *= 10;
significand -= F::small_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_upper64(x: u64, hi: u64, lo: u64) -> u64 {
let mut g0 = x as u128 * hi as u128;
g0 += umul128_upper64(x, lo) as u128;
(g0 >> 64) as u64
}
#[inline(always)]
pub const fn umul192_middle64(x: u64, hi: u64, lo: u64) -> u64 {
let g01 = x.wrapping_mul(hi);
let g10 = umul128_upper64(x, lo);
g01.wrapping_add(g10)
}
#[inline(always)]
pub const fn umul96_upper32(x: u64, y: u64) -> u64 {
umul128_upper64(x, 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(n: u64, exp: u32, max_pow2: i32, max_pow5: i32) -> u64 {
let pow2 = max_pow2 + (floor_log2_pow10(exp as i32 + max_pow5) - (exp as i32 + max_pow5));
if exp == 3 && pow2 < 70 {
umul128_upper64(n, 0x8312_6e97_8d4f_df3c) >> 9
} else {
n / pow64(10, exp)
}
}
macro_rules! mod_inverse {
($t:ident, $a:ident) => {{
let mut mod_inverse: $t = 1;
let mut i = 1;
while i < <$t as Integer>::BITS {
mod_inverse = mod_inverse.wrapping_mul(mod_inverse).wrapping_mul($a);
i += 1;
}
mod_inverse
}};
}
#[inline(always)]
pub const fn mod32_inverse(a: u32) -> u32 {
mod_inverse!(u32, a)
}
#[inline(always)]
pub const fn mod64_inverse(a: u64) -> u64 {
mod_inverse!(u64, a)
}
pub struct Div32Table<const N: usize> {
mod_inv: [u32; N],
max_quotients: [u32; N],
}
pub struct Div64Table<const N: usize> {
mod_inv: [u64; N],
max_quotients: [u64; N],
}
macro_rules! div_table {
($t:ident, $table:ident, $modular_inverse:ident, $a:ident) => {{
let mod_inverse = $modular_inverse($a);
let mut mod_inv = [0; N];
let mut max_quotients = [0; N];
let mut pow_of_mod_inverse: $t = 1;
let mut pow_of_a = 1;
let mut i = 0;
while i < N {
mod_inv[i] = pow_of_mod_inverse;
max_quotients[i] = $t::MAX / pow_of_a;
pow_of_mod_inverse = pow_of_mod_inverse.wrapping_mul(mod_inverse);
pow_of_a *= $a;
i += 1;
}
$table {
mod_inv,
max_quotients,
}
}};
}
#[inline(always)]
pub const fn div32_table<const N: usize>(a: u32) -> Div32Table<N> {
div_table!(u32, Div32Table, mod32_inverse, a)
}
#[inline(always)]
pub const fn div64_table<const N: usize>(a: u64) -> Div64Table<N> {
div_table!(u64, Div64Table, mod64_inverse, a)
}
impl RoundMode {
#[inline(always)]
pub const fn break_rounding_tie(&self, significand: u64) -> u64 {
match self {
RoundMode::Round => significand & !1u64,
RoundMode::Truncate => significand - 1u64,
}
}
}
#[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_minus_1: 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_minus_1)
}
#[inline(always)]
pub fn compute_right_endpoint_u64<F: DragonboxFloat>(pow5: u64, beta_minus_1: 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_minus_1)
}
#[inline(always)]
pub fn compute_round_up_u64<F: DragonboxFloat>(pow5: u64, beta_minus_1: i32) -> u64 {
let shift = 64 - F::MANTISSA_SIZE - 2;
((pow5 >> (shift - beta_minus_1)) + 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 fn max_power<F: DragonboxFloat>() -> i32 {
let max = F::Unsigned::MAX.as_u64();
let max_mantissa = max / pow64(10, F::KAPPA + 2);
let mut k = 0i32;
let mut p = 1u64;
while p < max_mantissa {
p *= 10;
k += 1;
}
k
}
macro_rules! div10 {
(@4 $table:ident, $n:ident, $quo:ident, $s:ident $(, $mul:ident)?) => {{
if $n & 0xf == 0 {
$quo = ($n >> 4).wrapping_mul($table.mod_inv[4]);
if ($quo <= $table.max_quotients[4]) {
$n = $quo;
$($mul = 10000;)?
$s |= 0x4;
}
}
}};
(@2 $table:ident, $n:ident, $quo:ident, $s:ident $(, $mul:ident)?) => {{
if $n & 0x3 == 0 {
$quo = ($n >> 2).wrapping_mul($table.mod_inv[2]);
if ($quo <= $table.max_quotients[2]) {
$n = $quo;
$($mul = if $s == 4 { 100 } else { 1000000 };)?
$s |= 0x2;
}
}
}};
(@1 $table:ident, $n:ident, $quo:ident, $s:ident $(, $mul:ident)?) => {{
if $n & 0x1 == 0 {
$quo = ($n >> 1).wrapping_mul($table.mod_inv[1]);
if ($quo <= $table.max_quotients[1]) {
$n = $quo;
$( $mul = ($mul >> 1).wrapping_mul($table.mod_inv[1]); )?
$s |= 0x1;
}
}
}};
}
macro_rules! divisible_by_pow5 {
(Self:: $table:ident, $x:ident, $exp:ident) => {{
debug_assert!(($exp as usize) < Self::$table.mod_inv.len());
let mod_inv = &Self::$table.mod_inv;
let max_quotients = &Self::$table.max_quotients;
let mod_inv = unsafe { index_unchecked!(mod_inv[$exp as usize]) };
let max_quo = unsafe { index_unchecked!(max_quotients[$exp as usize]) };
$x.wrapping_mul(mod_inv) <= max_quo
}};
}
struct Div10Info {
magic_number: u32,
bits_for_comparison: i32,
threshold: u32,
shift_amount: i32,
}
impl Div10Info {
#[inline(always)]
pub const fn comparison_mask(&self) -> u32 {
(1u32 << self.bits_for_comparison) - 1
}
}
const F32_DIV10_INFO: Div10Info = Div10Info {
magic_number: 0xcccd,
bits_for_comparison: 16,
threshold: 0x3333,
shift_amount: 19,
};
const F64_DIV10_INFO: Div10Info = Div10Info {
magic_number: 0x147c29,
bits_for_comparison: 12,
threshold: 0xa3,
shift_amount: 27,
};
macro_rules! check_div_pow10 {
($n:ident, $float:ident, $info:ident) => {{
let mut res = $n * $info.magic_number;
let shr = $float::KAPPA;
let shl = $info.bits_for_comparison as u32 - $float::KAPPA;
let c = ((res >> shr) | (res << shl)) & $info.comparison_mask();
res >>= $info.shift_amount;
(res, c <= $info.threshold)
}};
}
struct SmallDiv10Info {
magic_number: u32,
shift_amount: i32,
}
const SMALL_F32_DIV10_INFO: SmallDiv10Info = SmallDiv10Info {
magic_number: 0xcccd,
shift_amount: 19,
};
const SMALL_F64_DIV10_INFO: SmallDiv10Info = SmallDiv10Info {
magic_number: 0xa3d8,
shift_amount: 22,
};
macro_rules! small_div_pow10 {
($n:ident, $info:ident) => {{
($n * $info.magic_number) >> $info.shift_amount
}};
}
pub trait DragonboxFloat: Float {
const KAPPA: u32;
const DECIMAL_DIGITS: usize;
const MAX_POW5_FACTOR: i32 = floor_log5_pow2(Self::MANTISSA_SIZE + 2);
const TABLE_SIZE: usize = Self::MAX_POW5_FACTOR as usize + 1;
const DIV5_THRESHOLD: i32 = floor_log2_pow10(Self::MAX_POW5_FACTOR + Self::KAPPA as i32 + 1);
const DIV5_TABLE: Self::Table;
type Power;
type Table;
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;
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta_minus_1: i32) -> 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);
unsafe fn divisible_by_pow5(x: u64, exp: u32) -> bool;
#[inline(always)]
fn divisible_by_pow2(x: u64, exp: u32) -> bool {
x.trailing_zeros() >= exp
}
#[inline(always)]
fn is_product_fc_pm_half(two_f: u64, exponent: i32, minus_k: i32) -> bool {
let lower_threshold = -(Self::KAPPA as i32) - floor_log5_pow2(Self::KAPPA as i32);
let upper_threshold = floor_log2_pow10(Self::KAPPA as i32 + 1);
if exponent < lower_threshold {
false
} else if exponent <= upper_threshold {
true
} else if exponent > Self::DIV5_THRESHOLD {
false
} else {
debug_assert!(minus_k < Self::MAX_POW5_FACTOR + 1);
unsafe { Self::divisible_by_pow5(two_f, minus_k as u32) }
}
}
#[inline(always)]
fn is_product_fc(two_f: u64, exponent: i32, minus_k: i32) -> bool {
let lower_threshold = -(Self::KAPPA as i32) - 1 - floor_log5_pow2(Self::KAPPA as i32 + 1);
let upper_threshold = floor_log2_pow10(Self::KAPPA as i32 + 1);
if exponent > Self::DIV5_THRESHOLD {
false
} else if exponent > upper_threshold {
debug_assert!(minus_k < Self::MAX_POW5_FACTOR + 1);
unsafe { Self::divisible_by_pow5(two_f, minus_k as u32) }
} else if exponent >= lower_threshold {
true
} else {
Self::divisible_by_pow2(two_f, (minus_k - exponent + 1) as u32)
}
}
fn check_div_pow10(n: u32) -> (u32, bool);
fn small_div_pow10(n: u32) -> u32;
}
impl DragonboxFloat for f32 {
const KAPPA: u32 = 1;
const DECIMAL_DIGITS: usize = 9;
const DIV5_TABLE: Self::Table = div32_table::<{ Self::TABLE_SIZE }>(5);
type Power = u64;
type Table = Div32Table<{ Self::TABLE_SIZE }>;
#[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 {
umul96_upper32(u, *pow5)
}
#[inline(always)]
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta_minus_1: i32) -> bool {
((umul96_lower64(two_f, *pow5) >> (64 - beta_minus_1)) & 1) != 0
}
#[inline(always)]
fn compute_delta(pow5: &Self::Power, beta_minus_1: i32) -> u32 {
(*pow5 >> (64 - 1 - beta_minus_1)) 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!(max_power::<Self>() == 7);
let mut n = mantissa as u32;
let table = div32_table::<{ Self::DECIMAL_DIGITS }>(5);
let mut quo: u32;
let mut s: i32 = 0;
div10!(@4 table, n, quo, s);
div10!(@2 table, n, quo, s);
div10!(@1 table, n, quo, s);
(n as u64, s)
}
#[inline(always)]
unsafe fn divisible_by_pow5(x: u64, exp: u32) -> bool {
debug_assert!(x <= u32::MAX as u64);
let x = x as u32;
divisible_by_pow5!(Self::DIV5_TABLE, x, exp)
}
#[inline(always)]
fn check_div_pow10(n: u32) -> (u32, bool) {
check_div_pow10!(n, f32, F32_DIV10_INFO)
}
#[inline(always)]
fn small_div_pow10(n: u32) -> u32 {
small_div_pow10!(n, SMALL_F32_DIV10_INFO)
}
}
impl DragonboxFloat for f64 {
const KAPPA: u32 = 2;
const DECIMAL_DIGITS: usize = 17;
const DIV5_TABLE: Self::Table = div64_table::<{ Self::TABLE_SIZE }>(5);
type Power = (u64, u64);
type Table = Div64Table<{ Self::TABLE_SIZE }>;
#[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 {
umul192_upper64(u, high(pow5), low(pow5))
}
#[inline(always)]
fn compute_mul_parity(two_f: u64, pow5: &Self::Power, beta_minus_1: i32) -> bool {
((umul192_middle64(two_f, high(pow5), low(pow5)) >> (64 - beta_minus_1)) & 1) != 0
}
#[inline(always)]
fn compute_delta(pow5: &Self::Power, beta_minus_1: i32) -> u32 {
(high(pow5) >> (64 - 1 - beta_minus_1)) 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!(max_power::<Self>() == 16);
let mut n = mantissa;
let table = div32_table::<{ f32::DECIMAL_DIGITS }>(5);
let quo_pow10_8 = divide_by_pow10(n, 8, 54, 0) as u32;
let mut rem = n.wrapping_sub(100000000.wrapping_mul(quo_pow10_8 as u64)) as u32;
if rem == 0 {
let mut n32 = quo_pow10_8;
let mut quo32: u32;
if n32 & 0xff == 0 {
quo32 = (n32 >> 8).wrapping_mul(table.mod_inv[8]);
if quo32 <= table.max_quotients[8] {
n = quo32 as u64;
return (n, 16);
}
}
let mut s: i32 = 8;
div10!(@4 table, n32, quo32, s);
div10!(@2 table, n32, quo32, s);
div10!(@1 table, n32, quo32, s);
(n32 as u64, s)
} else {
let mut quo32: u32;
let mut mul: u32 = 100000000;
let mut s: i32 = 0;
div10!(@4 table, rem, quo32, s, mul);
div10!(@2 table, rem, quo32, s, mul);
div10!(@1 table, rem, quo32, s, mul);
let n = rem as u64 + quo_pow10_8 as u64 * mul as u64;
(n, s)
}
}
#[inline(always)]
unsafe fn divisible_by_pow5(x: u64, exp: u32) -> bool {
divisible_by_pow5!(Self::DIV5_TABLE, x, exp)
}
#[inline(always)]
fn check_div_pow10(n: u32) -> (u32, bool) {
check_div_pow10!(n, f64, F64_DIV10_INFO)
}
#[inline(always)]
fn small_div_pow10(n: u32) -> u32 {
small_div_pow10!(n, SMALL_F64_DIV10_INFO)
}
}
#[cfg(feature = "f16")]
macro_rules! dragonbox_unimpl {
($($t:ident)*) => ($(
impl DragonboxFloat for $t {
const KAPPA: u32 = 0;
const DECIMAL_DIGITS: usize = 0;
const DIV5_TABLE: Self::Table = 0;
type Power = u64;
type Table = u8;
#[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 {
unimplemented!()
}
#[inline(always)]
fn compute_mul(_: u64, _: &Self::Power) -> u64 {
unimplemented!()
}
#[inline(always)]
fn compute_mul_parity(_: u64, _: &Self::Power, _: i32) -> 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)]
unsafe fn divisible_by_pow5(_: u64, _: u32) -> bool {
unimplemented!()
}
#[inline(always)]
fn check_div_pow10(_: u32) -> (u32, bool) {
unimplemented!()
}
#[inline(always)]
fn small_div_pow10(_: u32) -> u32 {
unimplemented!()
}
}
)*);
}
#[cfg(feature = "f16")]
dragonbox_unimpl! { bf16 f16 }