#![cfg(not(feature = "compact"))]
#![doc(hidden)]
use crate::float::{ExtendedFloat80, LemireFloat};
use crate::number::Number;
use crate::shared;
use crate::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE};
#[must_use]
#[inline(always)]
pub fn lemire<F: LemireFloat>(num: &Number, lossy: bool) -> ExtendedFloat80 {
let mut fp = compute_float::<F>(num.exponent, num.mantissa, lossy);
if !lossy
&& num.many_digits
&& fp.exp >= 0
&& fp != compute_float::<F>(num.exponent, num.mantissa + 1, false)
{
fp = compute_error::<F>(num.exponent, num.mantissa);
}
fp
}
#[inline]
#[must_use]
#[allow(clippy::missing_inline_in_public_items)] pub fn compute_float<F: LemireFloat>(q: i64, mut w: u64, lossy: bool) -> ExtendedFloat80 {
let fp_zero = ExtendedFloat80 {
mant: 0,
exp: 0,
};
let fp_inf = ExtendedFloat80 {
mant: 0,
exp: F::INFINITE_POWER,
};
if w == 0 || q < F::SMALLEST_POWER_OF_TEN as i64 {
return fp_zero;
} else if q > F::LARGEST_POWER_OF_TEN as i64 {
return fp_inf;
}
let lz = w.leading_zeros() as i32;
w <<= lz;
let (lo, hi) = compute_product_approx(q, w, F::MANTISSA_SIZE as usize + 3);
if !lossy && lo == 0xFFFF_FFFF_FFFF_FFFF {
let inside_safe_exponent = (-27..=55).contains(&q);
if !inside_safe_exponent {
return compute_error_scaled::<F>(q, hi, lz);
}
}
let upperbit = (hi >> 63) as i32;
let mut mantissa = hi >> (upperbit + 64 - F::MANTISSA_SIZE - 3);
let mut power2 = power(q as i32) + upperbit - lz - F::MINIMUM_EXPONENT;
if power2 <= 0 {
if -power2 + 1 >= 64 {
return fp_zero;
}
mantissa >>= -power2 + 1;
mantissa += mantissa & 1;
mantissa >>= 1;
power2 = (mantissa >= (1_u64 << F::MANTISSA_SIZE)) as i32;
return ExtendedFloat80 {
mant: mantissa,
exp: power2,
};
}
if lo <= 1
&& q >= F::MIN_EXPONENT_ROUND_TO_EVEN as i64
&& q <= F::MAX_EXPONENT_ROUND_TO_EVEN as i64
&& mantissa & 3 == 1
&& (mantissa << (upperbit + 64 - F::MANTISSA_SIZE - 3)) == hi
{
mantissa &= !1_u64;
}
mantissa += mantissa & 1;
mantissa >>= 1;
if mantissa >= (2_u64 << F::MANTISSA_SIZE) {
mantissa = 1_u64 << F::MANTISSA_SIZE;
power2 += 1;
}
mantissa &= !(1_u64 << F::MANTISSA_SIZE);
if power2 >= F::INFINITE_POWER {
return fp_inf;
}
ExtendedFloat80 {
mant: mantissa,
exp: power2,
}
}
#[must_use]
#[inline(always)]
pub fn compute_error<F: LemireFloat>(q: i64, mut w: u64) -> ExtendedFloat80 {
let lz = w.leading_zeros() as i32;
w <<= lz;
let hi = compute_product_approx(q, w, F::MANTISSA_SIZE as usize + 3).1;
compute_error_scaled::<F>(q, hi, lz)
}
#[must_use]
#[inline(always)]
pub const fn compute_error_scaled<F: LemireFloat>(q: i64, mut w: u64, lz: i32) -> ExtendedFloat80 {
let hilz = (w >> 63) as i32 ^ 1;
w <<= hilz;
let power2 = power(q as i32) + F::EXPONENT_BIAS - hilz - lz - 62;
ExtendedFloat80 {
mant: w,
exp: power2 + shared::INVALID_FP,
}
}
#[inline(always)]
const fn power(q: i32) -> i32 {
(q.wrapping_mul(152_170 + 65536) >> 16) + 63
}
#[inline(always)]
const fn full_multiplication(a: u64, b: u64) -> (u64, u64) {
let r = (a as u128) * (b as u128);
(r as u64, (r >> 64) as u64)
}
#[inline]
fn compute_product_approx(q: i64, w: u64, precision: usize) -> (u64, u64) {
debug_assert!(q >= SMALLEST_POWER_OF_FIVE as i64, "must be within our required pow5 range");
debug_assert!(q <= LARGEST_POWER_OF_FIVE as i64, "must be within our required pow5 range");
debug_assert!(precision <= 64, "requires a 64-bit or smaller float");
let mask = if precision < 64 {
0xFFFF_FFFF_FFFF_FFFF_u64 >> precision
} else {
0xFFFF_FFFF_FFFF_FFFF_u64
};
let index = (q - SMALLEST_POWER_OF_FIVE as i64) as usize;
let (lo5, hi5) = POWER_OF_FIVE_128[index];
let (mut first_lo, mut first_hi) = full_multiplication(w, lo5);
if first_hi & mask == mask {
let (_, second_hi) = full_multiplication(w, hi5);
first_lo = first_lo.wrapping_add(second_hi);
if second_hi > first_lo {
first_hi += 1;
}
}
(first_lo, first_hi)
}