use crate::{
error::panic_unlimited_precision,
repr::{Context, Repr, Word},
round::{mode, Round},
utils::digit_len,
};
use dashu_base::Sign;
use dashu_int::{DoubleWord, IBig};
pub struct FBig<RoundingMode: Round = mode::Zero, const BASE: Word = 2> {
pub(crate) repr: Repr<BASE>,
pub(crate) context: Context<RoundingMode>,
}
impl<R: Round, const B: Word> FBig<R, B> {
#[inline]
pub(crate) const fn new(repr: Repr<B>, context: Context<R>) -> Self {
Self { repr, context }
}
#[inline]
pub fn from_repr(repr: Repr<B>, context: Context<R>) -> Self {
debug_assert!(
repr.is_infinite() || !context.is_limited() || repr.digits() <= context.precision + 1
);
Self { repr, context }
}
#[inline]
pub const fn from_repr_const(repr: Repr<B>) -> Self {
Self {
repr,
context: Context::new(0),
}
}
pub const ZERO: Self = Self::new(Repr::zero(), Context::new(0));
pub const ONE: Self = Self::new(Repr::one(), Context::new(0));
pub const NEG_ONE: Self = Self::new(Repr::neg_one(), Context::new(0));
pub const INFINITY: Self = Self::new(Repr::infinity(), Context::new(0));
pub const NEG_INFINITY: Self = Self::new(Repr::neg_infinity(), Context::new(0));
#[inline]
pub const fn precision(&self) -> usize {
self.context.precision
}
#[inline]
pub fn digits(&self) -> usize {
self.repr.digits()
}
#[inline]
pub const fn context(&self) -> Context<R> {
self.context
}
#[inline]
pub const fn repr(&self) -> &Repr<B> {
&self.repr
}
#[inline]
pub fn into_repr(self) -> Repr<B> {
self.repr
}
#[inline]
pub fn from_parts(significand: IBig, exponent: isize) -> Self {
let precision = digit_len::<B>(&significand).max(1); let repr = Repr::new(significand, exponent);
let context = Context::new(precision);
Self::new(repr, context)
}
#[inline]
pub const fn from_parts_const(
sign: Sign,
mut significand: DoubleWord,
mut exponent: isize,
min_precision: Option<usize>,
) -> Self {
if significand == 0 {
return Self::ZERO;
}
let mut digits = 0;
if B.is_power_of_two() {
let base_bits = B.trailing_zeros();
let shift = significand.trailing_zeros() / base_bits;
significand >>= shift * base_bits;
exponent += shift as isize;
digits = ((DoubleWord::BITS - significand.leading_zeros() + base_bits - 1) / base_bits)
as usize;
} else {
let mut pow: DoubleWord = 1;
while significand % (B as DoubleWord) == 0 {
significand /= B as DoubleWord;
exponent += 1;
}
while let Some(next) = pow.checked_mul(B as DoubleWord) {
digits += 1;
if next > significand {
break;
}
pow = next;
}
}
let repr = Repr {
significand: IBig::from_parts_const(sign, significand),
exponent,
};
let precision = match min_precision {
Some(prec) => {
if prec > digits {
prec
} else {
digits
}
}
None => digits,
};
Self::new(repr, Context::new(precision))
}
#[inline]
pub fn ulp(&self) -> Self {
if self.context.precision == 0 {
panic_unlimited_precision();
}
if self.repr.is_infinite() {
return self.clone();
}
let repr = Repr {
significand: IBig::ONE,
exponent: self.repr.exponent + self.repr.digits() as isize
- self.context.precision as isize,
};
Self::new(repr, self.context)
}
#[inline]
pub(crate) fn sub_ulp(&self) -> Self {
debug_assert!(self.context.precision != 0);
debug_assert!(self.repr.is_finite());
let repr = Repr {
significand: IBig::ONE,
exponent: self.repr.exponent + self.repr.digits_lb() as isize
- self.context.precision as isize
- 1,
};
Self::new(repr, self.context)
}
}
impl<R: Round, const B: Word> Clone for FBig<R, B> {
#[inline]
fn clone(&self) -> Self {
Self {
repr: self.repr.clone(),
context: self.context,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.repr.clone_from(&source.repr);
self.context = source.context;
}
}
impl<R: Round, const B: Word> Default for FBig<R, B> {
#[inline]
fn default() -> Self {
Self::ZERO
}
}