use crate::{
error::{assert_finite, FpError},
round::{Round, Rounded},
utils::{digit_len, split_digits, split_digits_ref},
};
use core::marker::PhantomData;
use dashu_base::{Approximation::*, EstimatedLog2, Sign};
pub use dashu_int::Word;
use dashu_int::{IBig, UBig};
pub struct Repr<const BASE: Word> {
pub(crate) significand: IBig,
pub(crate) exponent: isize,
}
impl<const B: Word> PartialEq for Repr<B> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if self.significand.is_zero() && other.significand.is_zero() {
let (self_inf, other_inf) = (self.is_infinite(), other.is_infinite());
match (self_inf, other_inf) {
(true, true) => self.sign() == other.sign(),
(false, false) => true, _ => false, }
} else {
self.significand == other.significand && self.exponent == other.exponent
}
}
}
impl<const B: Word> Eq for Repr<B> {}
#[derive(Clone, Copy)]
pub struct Context<RoundingMode: Round> {
pub(crate) precision: usize,
_marker: PhantomData<RoundingMode>,
}
#[inline]
const fn negate_special_exponent(exp: isize) -> isize {
match exp {
0 => -1,
-1 => 0,
isize::MAX => isize::MIN,
isize::MIN => isize::MAX,
other => -other,
}
}
fn rounded_to_repr<const B: Word>(
significand: IBig,
exponent: isize,
input_negative: bool,
) -> Repr<B> {
if significand.is_zero() && input_negative {
Repr::neg_zero()
} else {
Repr::new(significand, exponent)
}
}
impl<const B: Word> Repr<B> {
pub const BASE: UBig = UBig::from_word(B);
#[inline]
pub const fn zero() -> Self {
Self {
significand: IBig::ZERO,
exponent: 0,
}
}
#[inline]
pub const fn one() -> Self {
Self {
significand: IBig::ONE,
exponent: 0,
}
}
#[inline]
pub const fn neg_one() -> Self {
Self {
significand: IBig::NEG_ONE,
exponent: 0,
}
}
#[inline]
pub const fn infinity() -> Self {
Self {
significand: IBig::ZERO,
exponent: isize::MAX,
}
}
#[inline]
pub const fn neg_infinity() -> Self {
Self {
significand: IBig::ZERO,
exponent: isize::MIN,
}
}
#[inline]
pub const fn neg_zero() -> Self {
Self {
significand: IBig::ZERO,
exponent: -1,
}
}
#[inline]
pub const fn is_pos_zero(&self) -> bool {
self.significand.is_zero() && self.exponent == 0
}
#[inline]
pub const fn is_neg_zero(&self) -> bool {
self.significand.is_zero() && self.exponent == -1
}
#[inline]
pub const fn is_one(&self) -> bool {
self.significand.is_one() && self.exponent == 0
}
#[inline]
pub const fn is_infinite(&self) -> bool {
self.significand.is_zero() && (self.exponent == isize::MAX || self.exponent == isize::MIN)
}
#[inline]
pub const fn is_finite(&self) -> bool {
!self.is_infinite()
}
pub fn is_int(&self) -> bool {
if self.is_infinite() {
false
} else {
self.exponent >= 0
}
}
#[inline]
pub const fn sign(&self) -> Sign {
if self.significand.is_zero() {
if self.exponent >= 0 {
Sign::Positive
} else {
Sign::Negative
}
} else {
self.significand.sign()
}
}
#[inline]
pub(crate) fn neg(self) -> Self {
if self.significand.is_zero() {
Self {
significand: self.significand,
exponent: negate_special_exponent(self.exponent),
}
} else {
Self {
significand: -self.significand,
exponent: self.exponent,
}
}
}
pub(crate) fn check_finite_exponent(self) -> Result<Self, FpError> {
if !self.significand.is_zero() {
if self.exponent == isize::MAX {
Err(FpError::Overflow(self.sign()))
} else if self.exponent == isize::MIN {
Err(FpError::Underflow(self.sign()))
} else {
Ok(self)
}
} else {
Ok(self)
}
}
#[inline]
pub(crate) const fn infinity_with_sign(sign: Sign) -> Self {
match sign {
Sign::Positive => Self::infinity(),
Sign::Negative => Self::neg_infinity(),
}
}
#[inline]
pub(crate) const fn zero_with_sign(sign: Sign) -> Self {
match sign {
Sign::Positive => Self::zero(),
Sign::Negative => Self::neg_zero(),
}
}
pub(crate) fn normalize(self) -> Self {
if self.significand.is_zero() {
if self.exponent == 0
|| self.exponent == -1
|| self.exponent == isize::MAX
|| self.exponent == isize::MIN
{
return self;
}
return Self::zero();
}
let Self {
mut significand,
mut exponent,
} = self;
if B == 2 {
let shift = significand.trailing_zeros().unwrap();
significand >>= shift;
exponent = exponent.saturating_add(shift as isize);
} else if B.is_power_of_two() {
let bits = B.trailing_zeros() as usize;
let shift = significand.trailing_zeros().unwrap() / bits;
significand >>= shift * bits;
exponent = exponent.saturating_add(shift as isize);
} else {
let (sign, mut mag) = significand.into_parts();
let shift = mag.remove(&UBig::from_word(B)).unwrap();
exponent = exponent.saturating_add(shift as isize);
significand = IBig::from_parts(sign, mag);
}
Self {
significand,
exponent,
}
}
#[inline]
pub fn digits(&self) -> usize {
assert_finite(self);
digit_len::<B>(&self.significand)
}
#[inline]
pub fn digits_ub(&self) -> usize {
assert_finite(self);
if self.significand.is_zero() {
return 0;
}
let log = match B {
2 => self.significand.log2_bounds().1,
10 => self.significand.log2_bounds().1 * core::f32::consts::LOG10_2,
_ => self.significand.log2_bounds().1 / Self::BASE.log2_bounds().0,
};
log as usize + 1
}
#[inline]
pub fn digits_lb(&self) -> usize {
assert_finite(self);
if self.significand.is_zero() {
return 0;
}
let log = match B {
2 => self.significand.log2_bounds().0,
10 => self.significand.log2_bounds().0 * core::f32::consts::LOG10_2,
_ => self.significand.log2_bounds().0 / Self::BASE.log2_bounds().1,
};
log as usize
}
#[inline]
pub(crate) fn smaller_than_one(&self) -> bool {
debug_assert!(self.is_finite());
self.exponent + (self.digits_ub() as isize) < -1
}
#[inline]
pub fn new(significand: IBig, exponent: isize) -> Self {
Self {
significand,
exponent,
}
.normalize()
}
#[inline]
pub fn significand(&self) -> &IBig {
&self.significand
}
#[inline]
pub fn exponent(&self) -> isize {
self.exponent
}
#[inline]
pub fn into_parts(self) -> (IBig, isize) {
(self.significand, self.exponent)
}
#[doc(hidden)]
#[rustversion::since(1.64)]
#[inline]
pub const unsafe fn from_static_words(
sign: Sign,
significand: &'static [Word],
exponent: isize,
) -> Self {
let significand = IBig::from_static_words(sign, significand);
assert!(!significand.is_multiple_of_const(B as _));
Self {
significand,
exponent,
}
}
}
impl<const B: Word> Clone for Repr<B> {
#[inline]
fn clone(&self) -> Self {
Self {
significand: self.significand.clone(),
exponent: self.exponent,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.significand.clone_from(&source.significand);
self.exponent = source.exponent;
}
}
impl<R: Round> Context<R> {
#[inline]
pub const fn new(precision: usize) -> Self {
Self {
precision,
_marker: PhantomData,
}
}
#[inline]
pub const fn max(lhs: Self, rhs: Self) -> Self {
Self {
precision: if lhs.precision > rhs.precision {
lhs.precision
} else {
rhs.precision
},
_marker: PhantomData,
}
}
#[inline]
pub(crate) const fn is_limited(&self) -> bool {
self.precision != 0
}
#[inline]
pub const fn precision(&self) -> usize {
self.precision
}
pub(crate) fn repr_round<const B: Word>(&self, repr: Repr<B>) -> Rounded<Repr<B>> {
assert_finite(&repr);
if !self.is_limited() {
return Exact(repr);
}
let digits = repr.digits();
if digits > self.precision {
let shift = digits - self.precision;
let input_neg = repr.sign() == Sign::Negative;
let (signif_hi, signif_lo) = split_digits::<B>(repr.significand, shift);
let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
let sig = signif_hi + adjust;
let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
Inexact(result, adjust)
} else {
Exact(repr)
}
}
pub(crate) fn repr_round_ref<const B: Word>(&self, repr: &Repr<B>) -> Rounded<Repr<B>> {
assert_finite(repr);
if !self.is_limited() {
return Exact(repr.clone());
}
let digits = repr.digits();
if digits > self.precision {
let shift = digits - self.precision;
let input_neg = repr.sign() == Sign::Negative;
let (signif_hi, signif_lo) = split_digits_ref::<B>(&repr.significand, shift);
let adjust = R::round_fract::<B>(&signif_hi, signif_lo, shift);
let sig = signif_hi + adjust;
let result = rounded_to_repr(sig, repr.exponent + shift as isize, input_neg);
Inexact(result, adjust)
} else {
Exact(repr.clone())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use dashu_base::Sign;
#[test]
fn infinity_encoding() {
assert_eq!(Repr::<2>::infinity().exponent, isize::MAX);
assert_eq!(Repr::<10>::neg_infinity().exponent, isize::MIN);
assert!(Repr::<2>::infinity().is_infinite());
assert!(Repr::<10>::neg_infinity().is_infinite());
assert!(!Repr::<2>::infinity().is_finite());
assert_eq!(Repr::<2>::infinity().sign(), Sign::Positive);
assert_eq!(Repr::<10>::neg_infinity().sign(), Sign::Negative);
}
#[test]
fn neg_zero_encoding() {
assert_eq!(Repr::<2>::neg_zero().exponent, -1);
assert!(Repr::<2>::neg_zero().is_neg_zero());
assert!(!Repr::<2>::neg_zero().is_pos_zero());
assert!(!Repr::<2>::neg_zero().is_infinite());
assert_eq!(Repr::<2>::neg_zero().sign(), Sign::Negative);
assert_eq!(Repr::<2>::zero().sign(), Sign::Positive);
}
#[test]
fn normalize_preserves_specials() {
assert_eq!(Repr::<2>::infinity(), Repr::<2>::infinity().normalize());
assert_eq!(Repr::<10>::neg_infinity(), Repr::<10>::neg_infinity().normalize());
assert_eq!(Repr::<2>::zero(), Repr::<2>::zero().normalize());
let stray: Repr<2> = Repr {
significand: IBig::ZERO,
exponent: 7,
};
assert_eq!(Repr::<2>::zero(), stray.normalize());
let r: Repr<2> = Repr {
significand: IBig::from(0b10100i32),
exponent: 0,
};
let r = r.normalize();
assert_eq!(r.significand, IBig::from(0b101i32));
assert_eq!(r.exponent, 2);
}
}