use crate::defs::SignedWord;
use crate::defs::DEFAULT_P;
use crate::num::BigFloatNumber;
use crate::Consts;
use crate::Error;
use crate::Exponent;
use crate::Radix;
use crate::RoundingMode;
use crate::Sign;
use crate::Word;
use core::num::FpCategory;
use lazy_static::lazy_static;
#[cfg(feature = "std")]
use core::fmt::Write;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
pub const NAN: BigFloat = BigFloat {
inner: Flavor::NaN(None),
};
pub const INF_POS: BigFloat = BigFloat {
inner: Flavor::Inf(Sign::Pos),
};
pub const INF_NEG: BigFloat = BigFloat {
inner: Flavor::Inf(Sign::Neg),
};
lazy_static! {
pub static ref ONE: BigFloat = BigFloat { inner: Flavor::Value(BigFloatNumber::from_word(1, DEFAULT_P).expect("Constant ONE initialized")) };
pub static ref TWO: BigFloat = BigFloat { inner: Flavor::Value(BigFloatNumber::from_word(2, DEFAULT_P).expect("Constant TWO initialized")) };
}
#[derive(Debug)]
pub struct BigFloat {
inner: Flavor,
}
#[derive(Debug)]
enum Flavor {
Value(BigFloatNumber),
NaN(Option<Error>),
Inf(Sign), }
impl BigFloat {
pub fn new(p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::new(p), false, true)
}
pub fn from_f64(f: f64, p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::from_f64(p, f), false, true)
}
pub fn nan(err: Option<Error>) -> Self {
BigFloat {
inner: Flavor::NaN(err),
}
}
pub fn from_f32(f: f32, p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::from_f64(p, f as f64), false, true)
}
pub fn is_inf_pos(&self) -> bool {
matches!(self.inner, Flavor::Inf(Sign::Pos))
}
pub fn is_inf_neg(&self) -> bool {
matches!(self.inner, Flavor::Inf(Sign::Neg))
}
pub fn is_inf(&self) -> bool {
matches!(self.inner, Flavor::Inf(_))
}
pub fn is_nan(&self) -> bool {
matches!(self.inner, Flavor::NaN(_))
}
pub fn is_int(&self) -> bool {
match &self.inner {
Flavor::Value(v) => v.is_int(),
Flavor::NaN(_) => false,
Flavor::Inf(_) => false,
}
}
pub fn err(&self) -> Option<Error> {
match &self.inner {
Flavor::NaN(Some(e)) => Some(*e),
_ => None,
}
}
pub fn add(&self, d2: &Self, p: usize, rm: RoundingMode) -> Self {
self.add_op(d2, p, rm, false)
}
pub fn add_full_prec(&self, d2: &Self) -> Self {
self.add_op(d2, 0, RoundingMode::None, true)
}
fn add_op(&self, d2: &Self, p: usize, rm: RoundingMode, full_prec: bool) -> Self {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => Self::result_to_ext(
if full_prec { v1.add_full_prec(v2) } else { v1.add(v2, p, rm) },
v1.is_zero(),
v1.sign() == v2.sign(),
),
Flavor::Inf(s2) => BigFloat {
inner: Flavor::Inf(*s2),
},
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::Inf(s1) => match &d2.inner {
Flavor::Value(_) => BigFloat {
inner: Flavor::Inf(*s1),
},
Flavor::Inf(s2) => {
if *s1 != *s2 {
NAN
} else {
BigFloat {
inner: Flavor::Inf(*s2),
}
}
}
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn sub(&self, d2: &Self, p: usize, rm: RoundingMode) -> Self {
self.sub_op(d2, p, rm, false)
}
pub fn sub_full_prec(&self, d2: &Self) -> Self {
self.sub_op(d2, 0, RoundingMode::None, true)
}
fn sub_op(&self, d2: &Self, p: usize, rm: RoundingMode, full_prec: bool) -> Self {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => Self::result_to_ext(
if full_prec { v1.sub_full_prec(v2) } else { v1.sub(v2, p, rm) },
v1.is_zero(),
v1.sign() == v2.sign(),
),
Flavor::Inf(s2) => {
if s2.is_positive() {
INF_NEG
} else {
INF_POS
}
}
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::Inf(s1) => match &d2.inner {
Flavor::Value(_) => BigFloat {
inner: Flavor::Inf(*s1),
},
Flavor::Inf(s2) => {
if *s1 == *s2 {
NAN
} else {
BigFloat {
inner: Flavor::Inf(*s1),
}
}
}
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn mul(&self, d2: &Self, p: usize, rm: RoundingMode) -> Self {
self.mul_op(d2, p, rm, false)
}
pub fn mul_full_prec(&self, d2: &Self) -> Self {
self.mul_op(d2, 0, RoundingMode::None, true)
}
fn mul_op(&self, d2: &Self, p: usize, rm: RoundingMode, full_prec: bool) -> Self {
match &self.inner {
Flavor::Value(v1) => {
match &d2.inner {
Flavor::Value(v2) => Self::result_to_ext(
if full_prec { v1.mul_full_prec(v2) } else { v1.mul(v2, p, rm) },
v1.is_zero(),
v1.sign() == v2.sign(),
),
Flavor::Inf(s2) => {
if v1.is_zero() {
NAN
} else {
let s = if v1.sign() == *s2 { Sign::Pos } else { Sign::Neg };
BigFloat {
inner: Flavor::Inf(s),
}
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
Flavor::Inf(s1) => {
match &d2.inner {
Flavor::Value(v2) => {
if v2.is_zero() {
NAN
} else {
let s = if v2.sign() == *s1 { Sign::Pos } else { Sign::Neg };
BigFloat {
inner: Flavor::Inf(s),
}
}
}
Flavor::Inf(s2) => {
let s = if s1 == s2 { Sign::Pos } else { Sign::Neg };
BigFloat {
inner: Flavor::Inf(s),
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn div(&self, d2: &Self, p: usize, rm: RoundingMode) -> Self {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => {
Self::result_to_ext(v1.div(v2, p, rm), v1.is_zero(), v1.sign() == v2.sign())
}
Flavor::Inf(_) => Self::new(v1.mantissa_max_bit_len()),
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::Inf(s1) => match &d2.inner {
Flavor::Value(v) => {
if *s1 == v.sign() {
INF_POS
} else {
INF_NEG
}
}
Flavor::Inf(_) => NAN,
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn rem(&self, d2: &Self) -> Self {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => {
Self::result_to_ext(v1.rem(v2), v1.is_zero(), v1.sign() == v2.sign())
}
Flavor::Inf(_) => self.clone(),
Flavor::NaN(err) => Self::nan(*err),
},
Flavor::Inf(_) => NAN,
Flavor::NaN(err) => Self::nan(*err),
}
}
#[allow(clippy::should_implement_trait)]
pub fn cmp(&self, d2: &BigFloat) -> Option<SignedWord> {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => Some(v1.cmp(v2)),
Flavor::Inf(s2) => {
if *s2 == Sign::Pos {
Some(-1)
} else {
Some(1)
}
}
Flavor::NaN(_) => None,
},
Flavor::Inf(s1) => match &d2.inner {
Flavor::Value(_) => Some(*s1 as SignedWord),
Flavor::Inf(s2) => Some(*s1 as SignedWord - *s2 as SignedWord),
Flavor::NaN(_) => None,
},
Flavor::NaN(_) => None,
}
}
pub fn abs_cmp(&self, d2: &Self) -> Option<SignedWord> {
match &self.inner {
Flavor::Value(v1) => match &d2.inner {
Flavor::Value(v2) => Some(v1.cmp(v2)),
Flavor::Inf(_) => Some(-1),
Flavor::NaN(_) => None,
},
Flavor::Inf(_) => match &d2.inner {
Flavor::Value(_) => Some(1),
Flavor::Inf(_) => Some(0),
Flavor::NaN(_) => None,
},
Flavor::NaN(_) => None,
}
}
pub fn inv_sign(&mut self) {
match &mut self.inner {
Flavor::Value(v1) => v1.inv_sign(),
Flavor::Inf(s) => self.inner = Flavor::Inf(s.invert()),
Flavor::NaN(_) => {}
}
}
pub fn pow(&self, n: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
match &self.inner {
Flavor::Value(v1) => {
match &n.inner {
Flavor::Value(v2) => Self::result_to_ext(
v1.pow(v2, p, rm, cc),
v1.is_zero(),
v1.sign() == v2.sign(),
),
Flavor::Inf(s2) => {
let val = v1.cmp(&crate::common::consts::ONE);
if val > 0 {
BigFloat {
inner: Flavor::Inf(*s2),
}
} else if val < 0 {
Self::new(p)
} else {
Self::from_u8(1, p)
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
Flavor::Inf(s1) => {
match &n.inner {
Flavor::Value(v2) => {
if v2.is_zero() {
Self::from_u8(1, p)
} else if v2.is_positive() {
if s1.is_negative() && v2.is_odd_int() {
INF_NEG
} else {
INF_POS
}
} else {
Self::new(p)
}
}
Flavor::Inf(s2) => {
if s2.is_positive() {
INF_POS
} else {
Self::new(p)
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn powi(&self, n: usize, p: usize, rm: RoundingMode) -> Self {
match &self.inner {
Flavor::Value(v1) => Self::result_to_ext(v1.powi(n, p, rm), false, true),
Flavor::Inf(s1) => {
if n == 0 {
Self::from_u8(1, p)
} else if s1.is_negative() && (n & 1 == 1) {
INF_NEG
} else {
INF_POS
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn log(&self, n: &Self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
match &self.inner {
Flavor::Value(v1) => {
match &n.inner {
Flavor::Value(v2) => {
if v2.is_zero() {
return INF_NEG;
}
Self::result_to_ext(v1.log(v2, p, rm, cc), false, true)
}
Flavor::Inf(s2) => {
if s2.is_positive() {
Self::new(p)
} else {
NAN
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
Flavor::Inf(s1) => {
if *s1 == Sign::Neg {
NAN
} else {
match &n.inner {
Flavor::Value(v2) => {
if v2.exponent() <= 0 {
INF_NEG
} else {
INF_POS
}
}
Flavor::Inf(_) => NAN, Flavor::NaN(err) => Self::nan(*err),
}
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn is_positive(&self) -> bool {
match &self.inner {
Flavor::Value(v) => v.is_positive(),
Flavor::Inf(s) => *s == Sign::Pos,
Flavor::NaN(_) => false,
}
}
pub fn is_negative(&self) -> bool {
match &self.inner {
Flavor::Value(v) => v.is_negative(),
Flavor::Inf(s) => *s == Sign::Neg,
Flavor::NaN(_) => false,
}
}
pub fn is_subnormal(&self) -> bool {
if let Flavor::Value(v) = &self.inner {
return v.is_subnormal();
}
false
}
pub fn is_zero(&self) -> bool {
match &self.inner {
Flavor::Value(v) => v.is_zero(),
Flavor::Inf(_) => false,
Flavor::NaN(_) => false,
}
}
pub fn clamp(&self, min: &Self, max: &Self) -> Self {
if self.is_nan() || min.is_nan() || max.is_nan() || max.cmp(min).unwrap() < 0 {
NAN
} else if self.cmp(min).unwrap() < 0 {
min.clone()
} else if self.cmp(max).unwrap() > 0 {
max.clone()
} else {
self.clone()
}
}
pub fn max(&self, d1: &Self) -> Self {
if self.is_nan() || d1.is_nan() {
NAN
} else if self.cmp(d1).unwrap() < 0 {
d1.clone()
} else {
self.clone()
}
}
pub fn min(&self, d1: &Self) -> Self {
if self.is_nan() || d1.is_nan() {
NAN
} else if self.cmp(d1).unwrap() > 0 {
d1.clone()
} else {
self.clone()
}
}
pub fn signum(&self) -> Self {
if self.is_nan() {
NAN
} else if self.is_negative() {
let mut ret = Self::from_u8(1, DEFAULT_P);
ret.inv_sign();
ret
} else {
Self::from_u8(1, DEFAULT_P)
}
}
pub fn parse(s: &str, rdx: Radix, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
match crate::parser::parse(s, rdx) {
Ok(ps) => {
if ps.is_inf() {
if ps.sign() == Sign::Pos {
INF_POS
} else {
INF_NEG
}
} else if ps.is_nan() {
NAN
} else {
let (m, s, e) = ps.raw_parts();
Self::result_to_ext(
BigFloatNumber::convert_from_radix(s, m, e, rdx, p, rm, cc),
false,
true,
)
}
}
Err(e) => Self::nan(Some(e)),
}
}
#[cfg(feature = "std")]
pub(crate) fn write_str<T: Write>(
&self,
w: &mut T,
rdx: Radix,
rm: RoundingMode,
cc: &mut Consts,
) -> Result<(), core::fmt::Error> {
match &self.inner {
Flavor::Value(v) => match v.format(rdx, rm, cc) {
Ok(s) => w.write_str(&s),
Err(e) => match e {
Error::ExponentOverflow(s) => {
if s.is_positive() {
w.write_str("Inf")
} else {
w.write_str("-Inf")
}
}
_ => w.write_str("Err"),
},
},
Flavor::Inf(sign) => {
let s = if sign.is_negative() { "-Inf" } else { "Inf" };
w.write_str(s)
}
crate::ext::Flavor::NaN(_) => w.write_str("NaN"),
}
}
pub fn format(&self, rdx: Radix, rm: RoundingMode, cc: &mut Consts) -> Result<String, Error> {
let s = match &self.inner {
Flavor::Value(v) => match v.format(rdx, rm, cc) {
Ok(s) => return Ok(s),
Err(e) => match e {
Error::ExponentOverflow(s) => {
if s.is_positive() {
"Inf"
} else {
"-Inf"
}
}
_ => "Err",
},
},
Flavor::Inf(sign) => {
if sign.is_negative() {
"-Inf"
} else {
"Inf"
}
}
crate::ext::Flavor::NaN(_) => "NaN",
};
let mut ret = String::new();
ret.try_reserve_exact(s.len())?;
ret.push_str(s);
Ok(ret)
}
#[cfg(feature = "random")]
pub fn random_normal(p: usize, exp_from: Exponent, exp_to: Exponent) -> Self {
Self::result_to_ext(
BigFloatNumber::random_normal(p, exp_from, exp_to),
false,
true,
)
}
pub fn classify(&self) -> FpCategory {
match &self.inner {
Flavor::Value(v) => {
if v.is_subnormal() {
FpCategory::Subnormal
} else if v.is_zero() {
FpCategory::Zero
} else {
FpCategory::Normal
}
}
Flavor::Inf(_) => FpCategory::Infinite,
Flavor::NaN(_) => FpCategory::Nan,
}
}
pub fn atan(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
match &self.inner {
Flavor::Value(v) => Self::result_to_ext(v.atan(p, rm, cc), v.is_zero(), true),
Flavor::Inf(s) => Self::result_to_ext(Self::half_pi(*s, p, rm, cc), false, true),
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn tanh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
match &self.inner {
Flavor::Value(v) => Self::result_to_ext(v.tanh(p, rm, cc), v.is_zero(), true),
Flavor::Inf(s) => Self::from_i8(s.to_int(), p),
Flavor::NaN(err) => Self::nan(*err),
}
}
fn half_pi(
s: Sign,
p: usize,
rm: RoundingMode,
cc: &mut Consts,
) -> Result<BigFloatNumber, Error> {
let mut half_pi = cc.pi_num(p, rm)?;
half_pi.set_exponent(1);
half_pi.set_sign(s);
Ok(half_pi)
}
fn result_to_ext(
res: Result<BigFloatNumber, Error>,
is_dividend_zero: bool,
is_same_sign: bool,
) -> BigFloat {
match res {
Err(e) => match e {
Error::ExponentOverflow(s) => {
if s.is_positive() {
INF_POS
} else {
INF_NEG
}
}
Error::DivisionByZero => {
if is_dividend_zero {
NAN
} else if is_same_sign {
INF_POS
} else {
INF_NEG
}
}
Error::MemoryAllocation => Self::nan(Some(Error::MemoryAllocation)),
Error::InvalidArgument => Self::nan(Some(Error::InvalidArgument)),
},
Ok(v) => BigFloat {
inner: Flavor::Value(v),
},
}
}
pub fn exponent(&self) -> Option<Exponent> {
match &self.inner {
Flavor::Value(v) => Some(v.exponent()),
_ => None,
}
}
pub fn precision(&self) -> Option<usize> {
match &self.inner {
Flavor::Value(v) => Some(v.precision()),
_ => None,
}
}
pub fn max_value(p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::max_value(p), false, true)
}
pub fn min_value(p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::min_value(p), false, true)
}
pub fn min_positive(p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::min_positive(p), false, true)
}
pub fn min_positive_normal(p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::min_positive_normal(p), false, true)
}
pub fn from_word(d: Word, p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::from_word(d, p), false, true)
}
pub fn neg(&self) -> Self {
let mut ret = self.clone();
ret.inv_sign();
ret
}
pub fn as_raw_parts(&self) -> Option<(&[Word], usize, Sign, Exponent, bool)> {
if let Flavor::Value(v) = &self.inner {
Some(v.as_raw_parts())
} else {
None
}
}
pub fn from_raw_parts(m: &[Word], n: usize, s: Sign, e: Exponent, inexact: bool) -> Self {
Self::result_to_ext(
BigFloatNumber::from_raw_parts(m, n, s, e, inexact),
false,
true,
)
}
pub fn from_words(m: &[Word], s: Sign, e: Exponent) -> Self {
Self::result_to_ext(BigFloatNumber::from_words(m, s, e), false, true)
}
pub fn sign(&self) -> Option<Sign> {
match &self.inner {
Flavor::Value(v) => Some(v.sign()),
Flavor::Inf(s) => Some(*s),
Flavor::NaN(_) => None,
}
}
pub fn set_exponent(&mut self, e: Exponent) {
if let Flavor::Value(v) = &mut self.inner {
v.set_exponent(e)
}
}
pub fn mantissa_max_bit_len(&self) -> Option<usize> {
if let Flavor::Value(v) = &self.inner {
Some(v.mantissa_max_bit_len())
} else {
None
}
}
pub fn set_precision(&mut self, p: usize, rm: RoundingMode) -> Result<(), Error> {
if let Flavor::Value(v) = &mut self.inner {
v.set_precision(p, rm)
} else {
Ok(())
}
}
pub fn reciprocal(&self, p: usize, rm: RoundingMode) -> Self {
match &self.inner {
Flavor::Value(v) => Self::result_to_ext(v.reciprocal(p, rm), false, v.is_positive()),
Flavor::Inf(s) => {
let mut ret = Self::new(p);
ret.set_sign(*s);
ret
}
Flavor::NaN(err) => Self::nan(*err),
}
}
pub fn set_sign(&mut self, s: Sign) {
match &mut self.inner {
Flavor::Value(v) => v.set_sign(s),
Flavor::Inf(_) => self.inner = Flavor::Inf(s),
Flavor::NaN(_) => {}
};
}
pub fn mantissa_digits(&self) -> Option<&[Word]> {
if let Flavor::Value(v) = &self.inner {
Some(v.mantissa().digits())
} else {
None
}
}
pub fn convert_from_radix(
sign: Sign,
digits: &[u8],
e: Exponent,
rdx: Radix,
p: usize,
rm: RoundingMode,
cc: &mut Consts,
) -> Self {
Self::result_to_ext(
BigFloatNumber::convert_from_radix(sign, digits, e, rdx, p, rm, cc),
false,
true,
)
}
pub fn convert_to_radix(
&self,
rdx: Radix,
rm: RoundingMode,
cc: &mut Consts,
) -> Result<(Sign, Vec<u8>, Exponent), Error> {
match &self.inner {
Flavor::Value(v) => v.convert_to_radix(rdx, rm, cc),
Flavor::NaN(_) => Err(Error::InvalidArgument),
Flavor::Inf(_) => Err(Error::InvalidArgument),
}
}
pub fn inexact(&self) -> bool {
if let Flavor::Value(v) = &self.inner {
v.inexact()
} else {
false
}
}
pub fn set_inexact(&mut self, inexact: bool) {
if let Flavor::Value(v) = &mut self.inner {
v.set_inexact(inexact);
}
}
pub fn try_set_precision(&mut self, p: usize, rm: RoundingMode, s: usize) -> bool {
if let Flavor::Value(v) = &mut self.inner {
v.try_set_precision(p, rm, s).unwrap_or_else(|e| {
self.inner = Flavor::NaN(Some(e));
true
})
} else {
true
}
}
}
impl Clone for BigFloat {
fn clone(&self) -> Self {
match &self.inner {
Flavor::Value(v) => Self::result_to_ext(v.clone(), false, true),
Flavor::Inf(s) => {
if s.is_positive() {
INF_POS
} else {
INF_NEG
}
}
Flavor::NaN(err) => Self::nan(*err),
}
}
}
macro_rules! gen_wrapper_arg {
($comment:literal, $fname:ident, $ret:ty, $pos_inf:block, $neg_inf:block, $($arg:ident, $arg_type:ty),*) => {
#[doc=$comment]
pub fn $fname(&self$(,$arg: $arg_type)*) -> $ret {
match &self.inner {
Flavor::Value(v) => Self::result_to_ext(v.$fname($($arg,)*), v.is_zero(), true),
Flavor::Inf(s) => if s.is_positive() $pos_inf else $neg_inf,
Flavor::NaN(err) => Self::nan(*err),
}
}
};
}
macro_rules! gen_wrapper_arg_rm {
($comment:literal, $fname:ident, $ret:ty, $pos_inf:block, $neg_inf:block, $($arg:ident, $arg_type:ty),*) => {
#[doc=$comment]
pub fn $fname(&self$(,$arg: $arg_type)*, rm: RoundingMode) -> $ret {
match &self.inner {
Flavor::Value(v) => {
Self::result_to_ext(v.$fname($($arg,)* rm), v.is_zero(), true)
},
Flavor::Inf(s) => if s.is_positive() $pos_inf else $neg_inf,
Flavor::NaN(err) => Self::nan(*err),
}
}
};
}
macro_rules! gen_wrapper_arg_rm_cc {
($comment:literal, $fname:ident, $ret:ty, $pos_inf:block, $neg_inf:block, $($arg:ident, $arg_type:ty),*) => {
#[doc=$comment]
pub fn $fname(&self$(,$arg: $arg_type)*, rm: RoundingMode, cc: &mut Consts) -> $ret {
match &self.inner {
Flavor::Value(v) => {
Self::result_to_ext(v.$fname($($arg,)* rm, cc), v.is_zero(), true)
},
Flavor::Inf(s) => if s.is_positive() $pos_inf else $neg_inf,
Flavor::NaN(err) => Self::nan(*err),
}
}
};
}
macro_rules! gen_wrapper_log {
($comment:literal, $fname:ident, $ret:ty, $pos_inf:block, $neg_inf:block, $($arg:ident, $arg_type:ty),*) => {
#[doc=$comment]
pub fn $fname(&self$(,$arg: $arg_type)*, rm: RoundingMode, cc: &mut Consts) -> $ret {
match &self.inner {
Flavor::Value(v) => {
if v.is_zero() {
return INF_NEG;
}
Self::result_to_ext(v.$fname($($arg,)* rm, cc), v.is_zero(), true)
},
Flavor::Inf(s) => if s.is_positive() $pos_inf else $neg_inf,
Flavor::NaN(err) => Self::nan(*err),
}
}
};
}
impl BigFloat {
gen_wrapper_arg!(
"Returns the absolute value of `self`.",
abs,
Self,
{ INF_POS },
{ INF_POS },
);
gen_wrapper_arg!("Returns the integer part of `self`.", int, Self, { NAN }, {
NAN
},);
gen_wrapper_arg!(
"Returns the fractional part of `self`.",
fract,
Self,
{ NAN },
{ NAN },
);
gen_wrapper_arg!(
"Returns the smallest integer greater than or equal to `self`.",
ceil,
Self,
{ INF_POS },
{ INF_NEG },
);
gen_wrapper_arg!(
"Returns the largest integer less than or equal to `self`.",
floor,
Self,
{ INF_POS },
{ INF_NEG },
);
gen_wrapper_arg_rm!("Returns the rounded number with `n` binary positions in the fractional part of the number using rounding mode `rm`.",
round,
Self,
{ INF_POS },
{ INF_NEG },
n,
usize
);
gen_wrapper_arg_rm!(
"Computes the square root of a number with precision `p`. The result is rounded using the rounding mode `rm`.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
sqrt,
Self,
{ INF_POS },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm!(
"Computes the cube root of a number with precision `p`. The result is rounded using the rounding mode `rm`.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
cbrt,
Self,
{ INF_POS },
{ INF_NEG },
p,
usize
);
gen_wrapper_log!(
"Computes the natural logarithm of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
ln,
Self,
{ INF_POS },
{ NAN },
p,
usize
);
gen_wrapper_log!(
"Computes the logarithm base 2 of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
log2,
Self,
{ INF_POS },
{ NAN },
p,
usize
);
gen_wrapper_log!(
"Computes the logarithm base 10 of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
log10,
Self,
{ INF_POS },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes `e` to the power of `self` with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
exp,
Self,
{ INF_POS },
{ Self::new(p) },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the sine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
sin,
Self,
{ NAN },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the cosine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
cos,
Self,
{ NAN },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the tangent of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
tan,
Self,
{ NAN },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the arcsine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
asin,
Self,
{NAN},
{NAN},
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the arccosine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
acos,
Self,
{ NAN },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the hyperbolic sine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache cc for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
sinh,
Self,
{ INF_POS },
{ INF_NEG },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the hyperbolic cosine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache cc for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
cosh,
Self,
{ INF_POS },
{ INF_POS },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the hyperbolic arcsine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
asinh,
Self,
{ INF_POS },
{ INF_NEG },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the hyperbolic arccosine of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
acosh,
Self,
{ INF_POS },
{ NAN },
p,
usize
);
gen_wrapper_arg_rm_cc!(
"Computes the hyperbolic arctangent of a number with precision `p`. The result is rounded using the rounding mode `rm`.
This function requires constants cache `cc` for computing the result.
Precision is rounded upwards to the word size. The function returns NaN if the precision `p` is incorrect.",
atanh,
Self,
{ NAN },
{ NAN },
p,
usize
);
}
macro_rules! impl_int_conv {
($s:ty, $from_s:ident) => {
impl BigFloat {
pub fn $from_s(i: $s, p: usize) -> Self {
Self::result_to_ext(BigFloatNumber::$from_s(i, p), false, true)
}
}
};
}
impl_int_conv!(i8, from_i8);
impl_int_conv!(i16, from_i16);
impl_int_conv!(i32, from_i32);
impl_int_conv!(i64, from_i64);
impl_int_conv!(i128, from_i128);
impl_int_conv!(u8, from_u8);
impl_int_conv!(u16, from_u16);
impl_int_conv!(u32, from_u32);
impl_int_conv!(u64, from_u64);
impl_int_conv!(u128, from_u128);
impl From<BigFloatNumber> for BigFloat {
fn from(x: BigFloatNumber) -> Self {
BigFloat {
inner: Flavor::Value(x),
}
}
}
#[cfg(feature = "std")]
use core::{
fmt::{Binary, Display, Formatter, Octal, UpperHex},
str::FromStr,
};
use core::{cmp::Eq, cmp::Ordering, cmp::PartialEq, cmp::PartialOrd, ops::Neg};
impl Neg for BigFloat {
type Output = BigFloat;
fn neg(mut self) -> Self::Output {
self.inv_sign();
self
}
}
impl Neg for &BigFloat {
type Output = BigFloat;
fn neg(self) -> Self::Output {
let mut ret = self.clone();
ret.inv_sign();
ret
}
}
impl PartialEq for BigFloat {
fn eq(&self, other: &Self) -> bool {
let cmp_result = BigFloat::cmp(self, other);
matches!(cmp_result, Some(0))
}
}
impl<'a> PartialEq<&'a BigFloat> for BigFloat {
fn eq(&self, other: &&'a BigFloat) -> bool {
let cmp_result = BigFloat::cmp(self, other);
matches!(cmp_result, Some(0))
}
}
impl Eq for BigFloat {}
impl PartialOrd for BigFloat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let cmp_result = BigFloat::cmp(self, other);
match cmp_result {
Some(v) => {
if v > 0 {
Some(Ordering::Greater)
} else if v < 0 {
Some(Ordering::Less)
} else {
Some(Ordering::Equal)
}
}
None => None,
}
}
}
impl<'a> PartialOrd<&'a BigFloat> for BigFloat {
fn partial_cmp(&self, other: &&'a BigFloat) -> Option<Ordering> {
let cmp_result = BigFloat::cmp(self, other);
match cmp_result {
Some(v) => {
if v > 0 {
Some(Ordering::Greater)
} else if v < 0 {
Some(Ordering::Less)
} else {
Some(Ordering::Equal)
}
}
None => None,
}
}
}
impl Default for BigFloat {
fn default() -> BigFloat {
BigFloat::new(DEFAULT_P)
}
}
#[cfg(feature = "std")]
impl FromStr for BigFloat {
type Err = Error;
fn from_str(src: &str) -> Result<BigFloat, Self::Err> {
let bf = crate::common::consts::TENPOWERS.with(|tp| {
let cc = &mut tp.borrow_mut();
BigFloat::parse(src, Radix::Dec, usize::MAX, RoundingMode::ToEven, cc)
});
if bf.is_nan() {
if let Some(err) = bf.err() {
return Err(err);
}
}
Ok(bf)
}
}
macro_rules! impl_from {
($tt:ty, $fn:ident) => {
impl From<$tt> for BigFloat {
fn from(v: $tt) -> Self {
BigFloat::$fn(v, DEFAULT_P)
}
}
};
}
impl_from!(f32, from_f32);
impl_from!(f64, from_f64);
impl_from!(i8, from_i8);
impl_from!(i16, from_i16);
impl_from!(i32, from_i32);
impl_from!(i64, from_i64);
impl_from!(i128, from_i128);
impl_from!(u8, from_u8);
impl_from!(u16, from_u16);
impl_from!(u32, from_u32);
impl_from!(u64, from_u64);
impl_from!(u128, from_u128);
#[cfg(feature = "std")]
macro_rules! impl_format_rdx {
($trait:ty, $rdx:path) => {
impl $trait for BigFloat {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
crate::common::consts::TENPOWERS.with(|tp| {
let cc = &mut tp.borrow_mut();
self.write_str(f, $rdx, RoundingMode::ToEven, cc)
})
}
}
};
}
#[cfg(feature = "std")]
impl_format_rdx!(Binary, Radix::Bin);
#[cfg(feature = "std")]
impl_format_rdx!(Octal, Radix::Oct);
#[cfg(feature = "std")]
impl_format_rdx!(Display, Radix::Dec);
#[cfg(feature = "std")]
impl_format_rdx!(UpperHex, Radix::Hex);
pub trait FromExt<T> {
fn from_ext(v: T, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self;
}
impl<T> FromExt<T> for BigFloat
where
BigFloat: From<T>,
{
fn from_ext(v: T, p: usize, rm: RoundingMode, _cc: &mut Consts) -> Self {
let mut ret = BigFloat::from(v);
if let Err(err) = ret.set_precision(p, rm) {
BigFloat::nan(Some(err))
} else {
ret
}
}
}
impl FromExt<&str> for BigFloat {
fn from_ext(v: &str, p: usize, rm: RoundingMode, cc: &mut Consts) -> Self {
BigFloat::parse(v, crate::Radix::Dec, p, rm, cc)
}
}
#[cfg(test)]
mod tests {
use crate::common::util::rand_p;
use crate::defs::DEFAULT_P;
use crate::ext::ONE;
use crate::ext::TWO;
use crate::BigFloat;
use crate::Consts;
use crate::Error;
use crate::Radix;
use crate::Sign;
use crate::Word;
use crate::INF_NEG;
use crate::INF_POS;
use crate::NAN;
use crate::{defs::RoundingMode, WORD_BIT_SIZE};
use core::num::FpCategory;
#[cfg(feature = "std")]
use std::str::FromStr;
#[cfg(not(feature = "std"))]
use alloc::format;
#[test]
fn test_ext() {
let rm = RoundingMode::ToOdd;
let mut cc = Consts::new().unwrap();
let d1 = BigFloat::from_u8(1, rand_p());
assert!(!d1.is_inf());
assert!(!d1.is_nan());
assert!(!d1.is_inf_pos());
assert!(!d1.is_inf_neg());
assert!(d1.is_positive());
let mut d1 = d1.div(&BigFloat::new(rand_p()), rand_p(), rm);
assert!(d1.is_inf());
assert!(!d1.is_nan());
assert!(d1.is_inf_pos());
assert!(!d1.is_inf_neg());
assert!(d1.is_positive());
d1.inv_sign();
assert!(d1.is_inf());
assert!(!d1.is_nan());
assert!(!d1.is_inf_pos());
assert!(d1.is_inf_neg());
assert!(d1.is_negative());
let d1 = BigFloat::new(rand_p()).div(&BigFloat::new(rand_p()), rand_p(), rm);
assert!(!d1.is_inf());
assert!(d1.is_nan());
assert!(!d1.is_inf_pos());
assert!(!d1.is_inf_neg());
assert!(d1.sign().is_none());
for _ in 0..1000 {
let i = rand::random::<i64>();
let d1 = BigFloat::from_i64(i, rand_p());
let n1 = BigFloat::parse(&format!("{}", i), Radix::Dec, rand_p(), rm, &mut cc);
assert!(d1.cmp(&n1) == Some(0));
let i = rand::random::<u64>();
let d1 = BigFloat::from_u64(i, rand_p());
let n1 = BigFloat::parse(&format!("{}", i), Radix::Dec, rand_p(), rm, &mut cc);
assert!(d1.cmp(&n1) == Some(0));
let i = rand::random::<i128>();
let d1 = BigFloat::from_i128(i, rand_p());
let n1 = BigFloat::parse(&format!("{}", i), Radix::Dec, rand_p(), rm, &mut cc);
assert!(d1.cmp(&n1) == Some(0));
let i = rand::random::<u128>();
let d1 = BigFloat::from_u128(i, rand_p());
let n1 = BigFloat::parse(&format!("{}", i), Radix::Dec, rand_p(), rm, &mut cc);
assert!(d1.cmp(&n1) == Some(0));
}
assert!(ONE.exponent().is_some());
assert!(INF_POS.exponent().is_none());
assert!(INF_NEG.exponent().is_none());
assert!(NAN.exponent().is_none());
assert!(ONE.as_raw_parts().is_some());
assert!(INF_POS.as_raw_parts().is_none());
assert!(INF_NEG.as_raw_parts().is_none());
assert!(NAN.as_raw_parts().is_none());
assert!(ONE.add(&ONE, rand_p(), rm).cmp(&TWO) == Some(0));
assert!(ONE.add(&INF_POS, rand_p(), rm).is_inf_pos());
assert!(INF_POS.add(&ONE, rand_p(), rm).is_inf_pos());
assert!(ONE.add(&INF_NEG, rand_p(), rm).is_inf_neg());
assert!(INF_NEG.add(&ONE, rand_p(), rm).is_inf_neg());
assert!(INF_POS.add(&INF_POS, rand_p(), rm).is_inf_pos());
assert!(INF_POS.add(&INF_NEG, rand_p(), rm).is_nan());
assert!(INF_NEG.add(&INF_NEG, rand_p(), rm).is_inf_neg());
assert!(INF_NEG.add(&INF_POS, rand_p(), rm).is_nan());
assert!(ONE.add_full_prec(&ONE).cmp(&TWO) == Some(0));
assert!(ONE.add_full_prec(&INF_POS).is_inf_pos());
assert!(INF_POS.add_full_prec(&ONE).is_inf_pos());
assert!(ONE.add_full_prec(&INF_NEG).is_inf_neg());
assert!(INF_NEG.add_full_prec(&ONE).is_inf_neg());
assert!(INF_POS.add_full_prec(&INF_POS).is_inf_pos());
assert!(INF_POS.add_full_prec(&INF_NEG).is_nan());
assert!(INF_NEG.add_full_prec(&INF_NEG).is_inf_neg());
assert!(INF_NEG.add_full_prec(&INF_POS).is_nan());
assert!(ONE.sub_full_prec(&ONE).is_zero());
assert!(ONE.sub_full_prec(&INF_POS).is_inf_neg());
assert!(INF_POS.sub_full_prec(&ONE).is_inf_pos());
assert!(ONE.sub_full_prec(&INF_NEG).is_inf_pos());
assert!(INF_NEG.sub_full_prec(&ONE).is_inf_neg());
assert!(INF_POS.sub_full_prec(&INF_POS).is_nan());
assert!(INF_POS.sub_full_prec(&INF_NEG).is_inf_pos());
assert!(INF_NEG.sub_full_prec(&INF_NEG).is_nan());
assert!(INF_NEG.sub_full_prec(&INF_POS).is_inf_neg());
assert!(ONE.mul_full_prec(&ONE).cmp(&ONE) == Some(0));
assert!(ONE.mul_full_prec(&INF_POS).is_inf_pos());
assert!(INF_POS.mul_full_prec(&ONE).is_inf_pos());
assert!(ONE.mul_full_prec(&INF_NEG).is_inf_neg());
assert!(INF_NEG.mul_full_prec(&ONE).is_inf_neg());
assert!(INF_POS.mul_full_prec(&INF_POS).is_inf_pos());
assert!(INF_POS.mul_full_prec(&INF_NEG).is_inf_neg());
assert!(INF_NEG.mul_full_prec(&INF_NEG).is_inf_pos());
assert!(INF_NEG.mul_full_prec(&INF_POS).is_inf_neg());
assert!(TWO.sub(&ONE, rand_p(), rm).cmp(&ONE) == Some(0));
assert!(ONE.sub(&INF_POS, rand_p(), rm).is_inf_neg());
assert!(INF_POS.sub(&ONE, rand_p(), rm).is_inf_pos());
assert!(ONE.sub(&INF_NEG, rand_p(), rm).is_inf_pos());
assert!(INF_NEG.sub(&ONE, rand_p(), rm).is_inf_neg());
assert!(INF_POS.sub(&INF_POS, rand_p(), rm).is_nan());
assert!(INF_POS.sub(&INF_NEG, rand_p(), rm).is_inf_pos());
assert!(INF_NEG.sub(&INF_NEG, rand_p(), rm).is_nan());
assert!(INF_NEG.sub(&INF_POS, rand_p(), rm).is_inf_neg());
assert!(TWO.mul(&ONE, rand_p(), rm).cmp(&TWO) == Some(0));
assert!(ONE.mul(&INF_POS, rand_p(), rm).is_inf_pos());
assert!(INF_POS.mul(&ONE, rand_p(), rm).is_inf_pos());
assert!(ONE.mul(&INF_NEG, rand_p(), rm).is_inf_neg());
assert!(INF_NEG.mul(&ONE, rand_p(), rm).is_inf_neg());
assert!(ONE.neg().mul(&INF_POS, rand_p(), rm).is_inf_neg());
assert!(ONE.neg().mul(&INF_NEG, rand_p(), rm).is_inf_pos());
assert!(INF_POS.mul(&ONE.neg(), rand_p(), rm).is_inf_neg());
assert!(INF_NEG.mul(&ONE.neg(), rand_p(), rm).is_inf_pos());
assert!(INF_POS.mul(&INF_POS, rand_p(), rm).is_inf_pos());
assert!(INF_POS.mul(&INF_NEG, rand_p(), rm).is_inf_neg());
assert!(INF_NEG.mul(&INF_NEG, rand_p(), rm).is_inf_pos());
assert!(INF_NEG.mul(&INF_POS, rand_p(), rm).is_inf_neg());
assert!(INF_POS.mul(&BigFloat::new(rand_p()), rand_p(), rm).is_nan());
assert!(INF_NEG.mul(&BigFloat::new(rand_p()), rand_p(), rm).is_nan());
assert!(BigFloat::new(rand_p()).mul(&INF_POS, rand_p(), rm).is_nan());
assert!(BigFloat::new(rand_p()).mul(&INF_NEG, rand_p(), rm).is_nan());
assert!(TWO.div(&TWO, rand_p(), rm).cmp(&ONE) == Some(0));
assert!(TWO.div(&INF_POS, rand_p(), rm).is_zero());
assert!(INF_POS.div(&TWO, rand_p(), rm).is_inf_pos());
assert!(TWO.div(&INF_NEG, rand_p(), rm).is_zero());
assert!(INF_NEG.div(&TWO, rand_p(), rm).is_inf_neg());
assert!(TWO.neg().div(&INF_POS, rand_p(), rm).is_zero());
assert!(TWO.neg().div(&INF_NEG, rand_p(), rm).is_zero());
assert!(INF_POS.div(&TWO.neg(), rand_p(), rm).is_inf_neg());
assert!(INF_NEG.div(&TWO.neg(), rand_p(), rm).is_inf_pos());
assert!(INF_POS.div(&INF_POS, rand_p(), rm).is_nan());
assert!(INF_POS.div(&INF_NEG, rand_p(), rm).is_nan());
assert!(INF_NEG.div(&INF_NEG, rand_p(), rm).is_nan());
assert!(INF_NEG.div(&INF_POS, rand_p(), rm).is_nan());
assert!(INF_POS
.div(&BigFloat::new(rand_p()), rand_p(), rm)
.is_inf_pos());
assert!(INF_NEG
.div(&BigFloat::new(rand_p()), rand_p(), rm)
.is_inf_neg());
assert!(BigFloat::new(rand_p())
.div(&INF_POS, rand_p(), rm)
.is_zero());
assert!(BigFloat::new(rand_p())
.div(&INF_NEG, rand_p(), rm)
.is_zero());
assert!(TWO.rem(&TWO).is_zero());
assert!(TWO.rem(&INF_POS).cmp(&TWO) == Some(0));
assert!(INF_POS.rem(&TWO).is_nan());
assert!(TWO.rem(&INF_NEG).cmp(&TWO) == Some(0));
assert!(INF_NEG.rem(&TWO).is_nan());
assert!(TWO.neg().rem(&INF_POS).cmp(&TWO.neg()) == Some(0));
assert!(TWO.neg().rem(&INF_NEG).cmp(&TWO.neg()) == Some(0));
assert!(INF_POS.rem(&TWO.neg()).is_nan());
assert!(INF_NEG.rem(&TWO.neg()).is_nan());
assert!(INF_POS.rem(&INF_POS).is_nan());
assert!(INF_POS.rem(&INF_NEG).is_nan());
assert!(INF_NEG.rem(&INF_NEG).is_nan());
assert!(INF_NEG.rem(&INF_POS).is_nan());
assert!(INF_POS.rem(&BigFloat::new(rand_p())).is_nan());
assert!(INF_NEG.rem(&BigFloat::new(rand_p())).is_nan());
assert!(BigFloat::new(rand_p()).rem(&INF_POS).is_zero());
assert!(BigFloat::new(rand_p()).rem(&INF_NEG).is_zero());
for op in [BigFloat::add, BigFloat::sub, BigFloat::mul, BigFloat::div] {
assert!(op(&NAN, &ONE, rand_p(), rm).is_nan());
assert!(op(&ONE, &NAN, rand_p(), rm).is_nan());
assert!(op(&NAN, &INF_POS, rand_p(), rm).is_nan());
assert!(op(&INF_POS, &NAN, rand_p(), rm).is_nan());
assert!(op(&NAN, &INF_NEG, rand_p(), rm).is_nan());
assert!(op(&INF_NEG, &NAN, rand_p(), rm).is_nan());
assert!(op(&NAN, &NAN, rand_p(), rm).is_nan());
}
assert!(BigFloat::rem(&NAN, &ONE).is_nan());
assert!(BigFloat::rem(&ONE, &NAN).is_nan());
assert!(BigFloat::rem(&NAN, &INF_POS).is_nan());
assert!(BigFloat::rem(&INF_POS, &NAN).is_nan());
assert!(BigFloat::rem(&NAN, &INF_NEG).is_nan());
assert!(BigFloat::rem(&INF_NEG, &NAN).is_nan());
assert!(BigFloat::rem(&NAN, &NAN).is_nan());
for op in [BigFloat::add_full_prec, BigFloat::sub_full_prec, BigFloat::mul_full_prec] {
assert!(op(&NAN, &ONE).is_nan());
assert!(op(&ONE, &NAN).is_nan());
assert!(op(&NAN, &INF_POS).is_nan());
assert!(op(&INF_POS, &NAN).is_nan());
assert!(op(&NAN, &INF_NEG).is_nan());
assert!(op(&INF_NEG, &NAN).is_nan());
assert!(op(&NAN, &NAN).is_nan());
}
assert!(ONE.cmp(&ONE).unwrap() == 0);
assert!(ONE.cmp(&INF_POS).unwrap() < 0);
assert!(INF_POS.cmp(&ONE).unwrap() > 0);
assert!(INF_POS.cmp(&INF_POS).unwrap() == 0);
assert!(ONE.cmp(&INF_NEG).unwrap() > 0);
assert!(INF_NEG.cmp(&ONE).unwrap() < 0);
assert!(INF_NEG.cmp(&INF_NEG).unwrap() == 0);
assert!(INF_POS.cmp(&INF_NEG).unwrap() > 0);
assert!(INF_NEG.cmp(&INF_POS).unwrap() < 0);
assert!(INF_POS.cmp(&INF_POS).unwrap() == 0);
assert!(ONE.cmp(&NAN).is_none());
assert!(NAN.cmp(&ONE).is_none());
assert!(INF_POS.cmp(&NAN).is_none());
assert!(NAN.cmp(&INF_POS).is_none());
assert!(INF_NEG.cmp(&NAN).is_none());
assert!(NAN.cmp(&INF_NEG).is_none());
assert!(NAN.cmp(&NAN).is_none());
assert!(ONE.abs_cmp(&ONE).unwrap() == 0);
assert!(ONE.abs_cmp(&INF_POS).unwrap() < 0);
assert!(INF_POS.abs_cmp(&ONE).unwrap() > 0);
assert!(INF_POS.abs_cmp(&INF_POS).unwrap() == 0);
assert!(ONE.abs_cmp(&INF_NEG).unwrap() < 0);
assert!(INF_NEG.abs_cmp(&ONE).unwrap() > 0);
assert!(INF_NEG.abs_cmp(&INF_NEG).unwrap() == 0);
assert!(INF_POS.abs_cmp(&INF_NEG).unwrap() == 0);
assert!(INF_NEG.abs_cmp(&INF_POS).unwrap() == 0);
assert!(INF_POS.abs_cmp(&INF_POS).unwrap() == 0);
assert!(ONE.abs_cmp(&NAN).is_none());
assert!(NAN.abs_cmp(&ONE).is_none());
assert!(INF_POS.abs_cmp(&NAN).is_none());
assert!(NAN.abs_cmp(&INF_POS).is_none());
assert!(INF_NEG.abs_cmp(&NAN).is_none());
assert!(NAN.abs_cmp(&INF_NEG).is_none());
assert!(NAN.abs_cmp(&NAN).is_none());
assert!(ONE.is_positive());
assert!(!ONE.is_negative());
assert!(ONE.neg().is_negative());
assert!(!ONE.neg().is_positive());
assert!(!INF_POS.is_negative());
assert!(INF_POS.is_positive());
assert!(INF_NEG.is_negative());
assert!(!INF_NEG.is_positive());
assert!(!NAN.is_positive());
assert!(!NAN.is_negative());
assert!(ONE.pow(&ONE, rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(BigFloat::new(DEFAULT_P)
.pow(&INF_POS, rand_p(), rm, &mut cc)
.is_zero());
assert!(BigFloat::new(DEFAULT_P)
.pow(&INF_NEG, rand_p(), rm, &mut cc)
.is_zero());
assert!(ONE.pow(&INF_POS, rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(ONE.pow(&INF_NEG, rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(TWO.pow(&INF_POS, rand_p(), rm, &mut cc).is_inf_pos());
assert!(TWO.pow(&INF_NEG, rand_p(), rm, &mut cc).is_inf_neg());
assert!(INF_POS.pow(&ONE, rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_NEG.pow(&ONE, rand_p(), rm, &mut cc).is_inf_neg());
assert!(INF_NEG.pow(&TWO, rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_NEG
.pow(&BigFloat::from_f64(10.2, DEFAULT_P), rand_p(), rm, &mut cc)
.is_inf_pos());
assert!(INF_NEG
.pow(&BigFloat::from_f64(3.0, DEFAULT_P), rand_p(), rm, &mut cc)
.is_inf_neg());
assert!(INF_POS.pow(&ONE.neg(), rand_p(), rm, &mut cc).is_zero());
assert!(INF_NEG.pow(&ONE.neg(), rand_p(), rm, &mut cc).is_zero());
assert!(
INF_POS
.pow(&BigFloat::new(DEFAULT_P), rand_p(), rm, &mut cc)
.cmp(&ONE)
== Some(0)
);
assert!(
INF_NEG
.pow(&BigFloat::new(DEFAULT_P), rand_p(), rm, &mut cc)
.cmp(&ONE)
== Some(0)
);
assert!(INF_POS.pow(&INF_POS, rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_NEG.pow(&INF_POS, rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_POS.pow(&INF_NEG, rand_p(), rm, &mut cc).is_zero());
assert!(INF_NEG.pow(&INF_NEG, rand_p(), rm, &mut cc).is_zero());
let half = ONE.div(&TWO, rand_p(), rm);
assert!(TWO.log(&TWO, rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(TWO.log(&INF_POS, rand_p(), rm, &mut cc).is_zero());
assert!(TWO.log(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.log(&TWO, rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_NEG.log(&TWO, rand_p(), rm, &mut cc).is_nan());
assert!(half.log(&half, rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(half.log(&INF_POS, rand_p(), rm, &mut cc).is_zero());
assert!(half.log(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.log(&half, rand_p(), rm, &mut cc).is_inf_neg());
assert!(INF_NEG.log(&half, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.log(&INF_POS, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.log(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.log(&INF_POS, rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.log(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(TWO.log(&ONE, rand_p(), rm, &mut cc).is_inf_pos());
assert!(half.log(&ONE, rand_p(), rm, &mut cc).is_inf_pos());
assert!(ONE.log(&ONE, rand_p(), rm, &mut cc).is_nan());
assert!(BigFloat::from_f32(f32::NAN, DEFAULT_P).is_nan());
assert!(BigFloat::from_f32(f32::INFINITY, DEFAULT_P).is_inf_pos());
assert!(BigFloat::from_f32(f32::NEG_INFINITY, DEFAULT_P).is_inf_neg());
assert!(!BigFloat::from_f32(1.0, DEFAULT_P).is_nan());
assert!(BigFloat::from_f64(f64::NAN, DEFAULT_P).is_nan());
assert!(BigFloat::from_f64(f64::INFINITY, DEFAULT_P).is_inf_pos());
assert!(BigFloat::from_f64(f64::NEG_INFINITY, DEFAULT_P).is_inf_neg());
assert!(!BigFloat::from_f64(1.0, DEFAULT_P).is_nan());
assert!(ONE.pow(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.pow(&ONE, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.pow(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.pow(&INF_POS, rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.pow(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.pow(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.pow(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.powi(2, rand_p(), rm).is_nan());
assert!(NAN.powi(0, rand_p(), rm).is_nan());
assert!(INF_POS.powi(2, rand_p(), rm).is_inf_pos());
assert!(INF_POS.powi(3, rand_p(), rm).is_inf_pos());
assert!(INF_NEG.powi(4, rand_p(), rm).is_inf_pos());
assert!(INF_NEG.powi(5, rand_p(), rm).is_inf_neg());
assert!(INF_POS.powi(0, rand_p(), rm).cmp(&ONE) == Some(0));
assert!(INF_NEG.powi(0, rand_p(), rm).cmp(&ONE) == Some(0));
assert!(TWO.log(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.log(&TWO, rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.log(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.log(&INF_POS, rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.log(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.log(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(NAN.log(&NAN, rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.abs().is_inf_pos());
assert!(INF_POS.abs().is_inf_pos());
assert!(NAN.abs().is_nan());
assert!(INF_NEG.int().is_nan());
assert!(INF_POS.int().is_nan());
assert!(NAN.int().is_nan());
assert!(INF_NEG.fract().is_nan());
assert!(INF_POS.fract().is_nan());
assert!(NAN.fract().is_nan());
assert!(INF_NEG.ceil().is_inf_neg());
assert!(INF_POS.ceil().is_inf_pos());
assert!(NAN.ceil().is_nan());
assert!(INF_NEG.floor().is_inf_neg());
assert!(INF_POS.floor().is_inf_pos());
assert!(NAN.floor().is_nan());
for rm in [
RoundingMode::Up,
RoundingMode::Down,
RoundingMode::ToZero,
RoundingMode::FromZero,
RoundingMode::ToEven,
RoundingMode::ToOdd,
] {
assert!(INF_NEG.round(0, rm).is_inf_neg());
assert!(INF_POS.round(0, rm).is_inf_pos());
assert!(NAN.round(0, rm).is_nan());
}
assert!(INF_NEG.sqrt(rand_p(), rm).is_nan());
assert!(INF_POS.sqrt(rand_p(), rm).is_inf_pos());
assert!(NAN.sqrt(rand_p(), rm).is_nan());
assert!(INF_NEG.cbrt(rand_p(), rm).is_inf_neg());
assert!(INF_POS.cbrt(rand_p(), rm).is_inf_pos());
assert!(NAN.cbrt(rand_p(), rm).is_nan());
for op in [BigFloat::ln, BigFloat::log2, BigFloat::log10] {
assert!(op(&INF_NEG, rand_p(), rm, &mut cc).is_nan());
assert!(op(&INF_POS, rand_p(), rm, &mut cc).is_inf_pos());
assert!(op(&NAN, rand_p(), rm, &mut cc).is_nan());
}
assert!(INF_NEG.exp(rand_p(), rm, &mut cc).is_zero());
assert!(INF_POS.exp(rand_p(), rm, &mut cc).is_inf_pos());
assert!(NAN.exp(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.sin(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.sin(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.sin(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.cos(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.cos(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.cos(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.tan(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.tan(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.tan(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.asin(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.asin(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.asin(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.acos(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.acos(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.acos(rand_p(), rm, &mut cc).is_nan());
let p = rand_p();
let mut half_pi: BigFloat = cc.pi_num(p, rm).unwrap().into();
half_pi.set_exponent(1);
assert!(INF_NEG.atan(p, rm, &mut cc).cmp(&half_pi.neg()) == Some(0));
assert!(INF_POS.atan(p, rm, &mut cc).cmp(&half_pi) == Some(0));
assert!(NAN.atan(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.sinh(rand_p(), rm, &mut cc).is_inf_neg());
assert!(INF_POS.sinh(rand_p(), rm, &mut cc).is_inf_pos());
assert!(NAN.sinh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.cosh(rand_p(), rm, &mut cc).is_inf_pos());
assert!(INF_POS.cosh(rand_p(), rm, &mut cc).is_inf_pos());
assert!(NAN.cosh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.tanh(rand_p(), rm, &mut cc).cmp(&ONE.neg()) == Some(0));
assert!(INF_POS.tanh(rand_p(), rm, &mut cc).cmp(&ONE) == Some(0));
assert!(NAN.tanh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.asinh(rand_p(), rm, &mut cc).is_inf_neg());
assert!(INF_POS.asinh(rand_p(), rm, &mut cc).is_inf_pos());
assert!(NAN.asinh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.acosh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.acosh(rand_p(), rm, &mut cc).is_inf_pos());
assert!(NAN.acosh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.atanh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_POS.atanh(rand_p(), rm, &mut cc).is_nan());
assert!(NAN.atanh(rand_p(), rm, &mut cc).is_nan());
assert!(INF_NEG.reciprocal(rand_p(), rm).is_zero());
assert!(INF_POS.reciprocal(rand_p(), rm).is_zero());
assert!(NAN.reciprocal(rand_p(), rm).is_nan());
assert!(TWO.signum().cmp(&ONE) == Some(0));
assert!(TWO.neg().signum().cmp(&ONE.neg()) == Some(0));
assert!(INF_POS.signum().cmp(&ONE) == Some(0));
assert!(INF_NEG.signum().cmp(&ONE.neg()) == Some(0));
assert!(NAN.signum().is_nan());
let d1 = ONE.clone();
assert!(d1.exponent() == Some(1));
let words: &[Word] = {
#[cfg(not(target_arch = "x86"))]
{
&[0, 0x8000000000000000]
}
#[cfg(target_arch = "x86")]
{
&[0, 0, 0, 0x80000000]
}
};
assert!(d1.mantissa_digits() == Some(words));
assert!(d1.mantissa_max_bit_len() == Some(DEFAULT_P));
assert!(d1.precision() == Some(DEFAULT_P));
assert!(d1.sign() == Some(Sign::Pos));
assert!(INF_POS.exponent().is_none());
assert!(INF_POS.mantissa_digits().is_none());
assert!(INF_POS.mantissa_max_bit_len().is_none());
assert!(INF_POS.precision().is_none());
assert!(INF_POS.sign() == Some(Sign::Pos));
assert!(INF_NEG.exponent().is_none());
assert!(INF_NEG.mantissa_digits().is_none());
assert!(INF_NEG.mantissa_max_bit_len().is_none());
assert!(INF_NEG.precision().is_none());
assert!(INF_NEG.sign() == Some(Sign::Neg));
assert!(NAN.exponent().is_none());
assert!(NAN.mantissa_digits().is_none());
assert!(NAN.mantissa_max_bit_len().is_none());
assert!(NAN.precision().is_none());
assert!(NAN.sign().is_none());
INF_POS.clone().set_exponent(1);
INF_POS.clone().set_precision(1, rm).unwrap();
INF_POS.clone().set_sign(Sign::Pos);
INF_NEG.clone().set_exponent(1);
INF_NEG.clone().set_precision(1, rm).unwrap();
INF_NEG.clone().set_sign(Sign::Pos);
NAN.clone().set_exponent(1);
NAN.clone().set_precision(1, rm).unwrap();
NAN.clone().set_sign(Sign::Pos);
assert!(INF_POS.min(&ONE).cmp(&ONE) == Some(0));
assert!(INF_NEG.min(&ONE).is_inf_neg());
assert!(NAN.min(&ONE).is_nan());
assert!(ONE.min(&INF_POS).cmp(&ONE) == Some(0));
assert!(ONE.min(&INF_NEG).is_inf_neg());
assert!(ONE.min(&NAN).is_nan());
assert!(NAN.min(&INF_POS).is_nan());
assert!(NAN.min(&INF_NEG).is_nan());
assert!(NAN.min(&NAN).is_nan());
assert!(INF_NEG.min(&INF_POS).is_inf_neg());
assert!(INF_POS.min(&INF_NEG).is_inf_neg());
assert!(INF_POS.min(&INF_POS).is_inf_pos());
assert!(INF_NEG.min(&INF_NEG).is_inf_neg());
assert!(INF_POS.max(&ONE).is_inf_pos());
assert!(INF_NEG.max(&ONE).cmp(&ONE) == Some(0));
assert!(NAN.max(&ONE).is_nan());
assert!(ONE.max(&INF_POS).is_inf_pos());
assert!(ONE.max(&INF_NEG).cmp(&ONE) == Some(0));
assert!(ONE.max(&NAN).is_nan());
assert!(NAN.max(&INF_POS).is_nan());
assert!(NAN.max(&INF_NEG).is_nan());
assert!(NAN.max(&NAN).is_nan());
assert!(INF_NEG.max(&INF_POS).is_inf_pos());
assert!(INF_POS.max(&INF_NEG).is_inf_pos());
assert!(INF_POS.max(&INF_POS).is_inf_pos());
assert!(INF_NEG.max(&INF_NEG).is_inf_neg());
assert!(ONE.clamp(&ONE.neg(), &TWO).cmp(&ONE) == Some(0));
assert!(ONE.clamp(&TWO, &ONE).is_nan());
assert!(ONE.clamp(&INF_POS, &ONE).is_nan());
assert!(ONE.clamp(&TWO, &INF_NEG).is_nan());
assert!(ONE.neg().clamp(&ONE, &TWO).cmp(&ONE) == Some(0));
assert!(TWO.clamp(&ONE.neg(), &ONE).cmp(&ONE) == Some(0));
assert!(INF_POS.clamp(&ONE, &TWO).cmp(&TWO) == Some(0));
assert!(INF_POS.clamp(&ONE, &INF_POS).is_inf_pos());
assert!(INF_POS.clamp(&INF_NEG, &ONE).cmp(&ONE) == Some(0));
assert!(INF_POS.clamp(&NAN, &INF_POS).is_nan());
assert!(INF_POS.clamp(&ONE, &NAN).is_nan());
assert!(INF_POS.clamp(&NAN, &NAN).is_nan());
assert!(INF_NEG.clamp(&ONE, &TWO).cmp(&ONE) == Some(0));
assert!(INF_NEG.clamp(&ONE, &INF_POS).cmp(&ONE) == Some(0));
assert!(INF_NEG.clamp(&INF_NEG, &ONE).is_inf_neg());
assert!(INF_NEG.clamp(&NAN, &INF_POS).is_nan());
assert!(INF_NEG.clamp(&ONE, &NAN).is_nan());
assert!(INF_NEG.clamp(&NAN, &NAN).is_nan());
assert!(NAN.clamp(&ONE, &TWO).is_nan());
assert!(NAN.clamp(&NAN, &TWO).is_nan());
assert!(NAN.clamp(&ONE, &NAN).is_nan());
assert!(NAN.clamp(&NAN, &NAN).is_nan());
assert!(NAN.clamp(&INF_NEG, &INF_POS).is_nan());
assert!(BigFloat::min_positive(DEFAULT_P).classify() == FpCategory::Subnormal);
assert!(INF_POS.classify() == FpCategory::Infinite);
assert!(INF_NEG.classify() == FpCategory::Infinite);
assert!(NAN.classify() == FpCategory::Nan);
assert!(ONE.classify() == FpCategory::Normal);
assert!(!INF_POS.is_subnormal());
assert!(!INF_NEG.is_subnormal());
assert!(!NAN.is_subnormal());
assert!(BigFloat::min_positive(DEFAULT_P).is_subnormal());
assert!(!BigFloat::min_positive_normal(DEFAULT_P).is_subnormal());
assert!(!BigFloat::max_value(DEFAULT_P).is_subnormal());
assert!(!BigFloat::min_value(DEFAULT_P).is_subnormal());
let n1 = BigFloat::convert_from_radix(
Sign::Pos,
&[],
0,
Radix::Dec,
usize::MAX - 1,
RoundingMode::None,
&mut cc,
);
assert!(n1.is_nan());
assert!(n1.err() == Some(Error::InvalidArgument));
assert!(
n1.convert_to_radix(Radix::Dec, RoundingMode::None, &mut cc)
== Err(Error::InvalidArgument)
);
assert!(
INF_POS.convert_to_radix(Radix::Dec, RoundingMode::None, &mut cc)
== Err(Error::InvalidArgument)
);
assert!(
INF_NEG.convert_to_radix(Radix::Dec, RoundingMode::None, &mut cc)
== Err(Error::InvalidArgument)
);
}
#[cfg(feature = "std")]
#[test]
fn test_ops_std() {
let mut cc = Consts::new().unwrap();
let d1 = BigFloat::parse(
"0.0123456789012345678901234567890123456789",
Radix::Dec,
DEFAULT_P,
RoundingMode::None,
&mut cc,
);
let d1str = format!("{}", d1);
assert_eq!(&d1str, "1.23456789012345678901234567890123456789e-2");
let mut d2 = BigFloat::from_str(&d1str).unwrap();
d2.set_precision(DEFAULT_P, RoundingMode::ToEven).unwrap();
assert_eq!(d2, d1);
let d1 = BigFloat::parse(
"-123.456789012345678901234567890123456789",
Radix::Dec,
DEFAULT_P,
RoundingMode::None,
&mut cc,
);
let d1str = format!("{}", d1);
assert_eq!(&d1str, "-1.23456789012345678901234567890123456789e+2");
let mut d2 = BigFloat::from_str(&d1str).unwrap();
d2.set_precision(DEFAULT_P, RoundingMode::ToEven).unwrap();
assert_eq!(d2, d1);
let d1str = format!("{}", INF_POS);
assert_eq!(d1str, "Inf");
let d1str = format!("{}", INF_NEG);
assert_eq!(d1str, "-Inf");
let d1str = format!("{}", NAN);
assert_eq!(d1str, "NaN");
assert!(BigFloat::from_str("abc").is_ok());
assert!(BigFloat::from_str("abc").unwrap().is_nan());
}
#[test]
pub fn test_ops() {
let mut cc = Consts::new().unwrap();
let d1 = -&(TWO.clone());
assert!(d1.is_negative());
let p = DEFAULT_P;
let rm = RoundingMode::ToEven;
assert!(
BigFloat::from_i8(-123, p) == BigFloat::parse("-1.23e+2", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_u8(123, p) == BigFloat::parse("1.23e+2", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_i16(-12312, p)
== BigFloat::parse("-1.2312e+4", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_u16(12312, p)
== BigFloat::parse("1.2312e+4", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_i32(-123456789, p)
== BigFloat::parse("-1.23456789e+8", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_u32(123456789, p)
== BigFloat::parse("1.23456789e+8", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_i64(-1234567890123456789, p)
== BigFloat::parse("-1.234567890123456789e+18", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_u64(1234567890123456789, p)
== BigFloat::parse("1.234567890123456789e+18", Radix::Dec, p, rm, &mut cc)
);
assert!(
BigFloat::from_i128(-123456789012345678901234567890123456789, p)
== BigFloat::parse(
"-1.23456789012345678901234567890123456789e+38",
Radix::Dec,
p,
rm,
&mut cc
)
);
assert!(
BigFloat::from_u128(123456789012345678901234567890123456789, p)
== BigFloat::parse(
"1.23456789012345678901234567890123456789e+38",
Radix::Dec,
p,
rm,
&mut cc
)
);
let neg = BigFloat::from_i8(-3, WORD_BIT_SIZE);
let pos = BigFloat::from_i8(5, WORD_BIT_SIZE);
assert!(pos > neg);
assert!(neg < pos);
assert!(!(pos < neg));
assert!(!(neg > pos));
assert!(INF_NEG < neg);
assert!(INF_NEG < pos);
assert!(INF_NEG < INF_POS);
assert!(!(INF_NEG > neg));
assert!(!(INF_NEG > pos));
assert!(!(INF_NEG > INF_POS));
assert!(INF_POS > neg);
assert!(INF_POS > pos);
assert!(INF_POS > INF_NEG);
assert!(!(INF_POS < neg));
assert!(!(INF_POS < pos));
assert!(!(INF_POS < INF_NEG));
assert!(!(INF_POS > INF_POS));
assert!(!(INF_POS < INF_POS));
assert!(!(INF_NEG > INF_NEG));
assert!(!(INF_NEG < INF_NEG));
assert!(!(INF_POS > NAN));
assert!(!(INF_POS < NAN));
assert!(!(INF_NEG > NAN));
assert!(!(INF_NEG < NAN));
assert!(!(NAN > INF_POS));
assert!(!(NAN < INF_POS));
assert!(!(NAN > INF_NEG));
assert!(!(NAN < INF_NEG));
assert!(!(NAN > NAN));
assert!(!(NAN < NAN));
assert!(!(neg > NAN));
assert!(!(neg < NAN));
assert!(!(pos > NAN));
assert!(!(pos < NAN));
assert!(!(NAN > neg));
assert!(!(NAN < neg));
assert!(!(NAN > pos));
assert!(!(NAN < pos));
assert!(!(NAN == NAN));
assert!(!(NAN == INF_POS));
assert!(!(NAN == INF_NEG));
assert!(!(INF_POS == NAN));
assert!(!(INF_NEG == NAN));
assert!(!(INF_NEG == INF_POS));
assert!(!(INF_POS == INF_NEG));
assert!(!(INF_POS == neg));
assert!(!(INF_POS == pos));
assert!(!(INF_NEG == neg));
assert!(!(INF_NEG == pos));
assert!(!(neg == INF_POS));
assert!(!(pos == INF_POS));
assert!(!(neg == INF_NEG));
assert!(!(pos == INF_NEG));
assert!(!(pos == neg));
assert!(!(neg == pos));
assert!(neg == neg);
assert!(pos == pos);
assert!(INF_NEG == INF_NEG);
assert!(INF_POS == INF_POS);
}
}
#[cfg(feature = "random")]
#[cfg(test)]
mod rand_tests {
use super::*;
use crate::defs::EXPONENT_MAX;
#[test]
fn test_rand() {
for _ in 0..1000 {
let p = rand::random::<usize>() % 1000 + DEFAULT_P;
let exp_from;
#[cfg(not(target_arch = "x86"))]
{
exp_from = rand::random::<Exponent>().abs();
}
#[cfg(target_arch = "x86")]
{
use crate::defs::EXPONENT_MIN;
exp_from =
rand::random::<Exponent>().abs() % (EXPONENT_MAX - EXPONENT_MIN) + EXPONENT_MIN;
}
let exp_shift = if EXPONENT_MAX > exp_from {
rand::random::<Exponent>().abs()
% (EXPONENT_MAX as isize - exp_from as isize) as Exponent
} else {
0
};
let exp_to = (exp_from as isize + exp_shift as isize) as Exponent;
let n = BigFloat::random_normal(p, exp_from, exp_to);
assert!(!n.is_subnormal());
assert!(n.exponent().unwrap() >= exp_from && n.exponent().unwrap() <= exp_to);
assert!(n.precision().unwrap() >= p);
}
}
}