pub(crate) mod arith;
pub(crate) mod big;
mod cmp;
mod ord;
#[cfg(feature = "serde")]
mod serde;
pub(crate) mod small;
mod traits;
pub use crate::float::big::ParseFloatError;
pub use crate::float::ord::OrdFloat;
pub use crate::float::small::{SmallFloat, ToSmall};
use gmp_mpfr_sys::mpfr;
use std::{i32, u32};
#[inline]
pub fn exp_min() -> i32 {
let min = unsafe { mpfr::get_emin() };
if min > mpfr::exp_t::from(i32::MIN) {
min as i32
} else {
i32::MIN
}
}
#[inline]
pub fn exp_max() -> i32 {
let max = unsafe { mpfr::get_emax() };
if max < mpfr::exp_t::from(i32::MAX) {
max as i32
} else {
i32::MAX
}
}
#[inline]
pub const fn prec_min() -> u32 {
mpfr::PREC_MIN as u32
}
#[inline]
pub const fn prec_max() -> u32 {
const MAX_FITS: bool = mpfr::PREC_MAX < u32::MAX as mpfr::prec_t;
const VALUES: [u32; 2] = [u32::MAX, mpfr::PREC_MAX as u32];
VALUES[MAX_FITS as usize]
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Round {
Nearest,
Zero,
Up,
Down,
#[doc(hidden)]
__Nonexhaustive,
}
impl Default for Round {
#[inline]
fn default() -> Round {
Round::Nearest
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Constant {
Log2,
Pi,
Euler,
Catalan,
#[doc(hidden)]
__Nonexhaustive,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Special {
Zero,
NegZero,
Infinity,
NegInfinity,
Nan,
#[doc(hidden)]
__Nonexhaustive,
}
#[cfg(test)]
#[allow(clippy::cognitive_complexity, clippy::float_cmp)]
pub(crate) mod tests {
use crate::float::{Round, Special};
#[cfg(feature = "rand")]
use crate::rand::{RandGen, RandState};
use crate::{Assign, Float};
use gmp_mpfr_sys::{gmp, mpfr};
use std::cmp::Ordering;
use std::f64;
use std::fmt::{Debug, Error as FmtError, Formatter};
#[derive(Clone, Copy)]
pub enum Cmp {
F64(f64),
Nan(bool),
}
impl Cmp {
pub fn inf(neg: bool) -> Cmp {
Cmp::F64(if neg {
f64::NEG_INFINITY
} else {
f64::INFINITY
})
}
}
impl Debug for Cmp {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match *self {
Cmp::F64(ref val) => val.fmt(f),
Cmp::Nan(negative) => {
let s = if negative { "-NaN" } else { "NaN" };
s.fmt(f)
}
}
}
}
impl PartialEq<Cmp> for Float {
fn eq(&self, other: &Cmp) -> bool {
match *other {
Cmp::F64(ref f) => self.eq(f),
Cmp::Nan(negative) => self.is_nan() && self.is_sign_negative() == negative,
}
}
}
#[test]
fn check_from_str() {
assert!(Float::with_val(53, Float::parse("-0").unwrap()).is_sign_negative());
assert!(Float::with_val(53, Float::parse("+0").unwrap()).is_sign_positive());
assert!(Float::with_val(53, Float::parse("1e1000").unwrap()).is_finite());
let huge_hex = "1@99999999999999999999999999999999";
assert!(Float::with_val(53, Float::parse_radix(huge_hex, 16).unwrap()).is_infinite());
let bad_strings = [
("", 10),
("-", 10),
("+", 10),
(".", 10),
("inf", 11),
("@ nan @", 10),
("inf", 16),
("1.1.", 10),
("1e", 10),
("e10", 10),
(".e10", 10),
("1e1.", 10),
("1e1e1", 10),
("1e+-1", 10),
("1e-+1", 10),
("+-1", 10),
("-+1", 10),
("infinit", 10),
("1@1a", 16),
("9", 9),
("nan(20) x", 10),
];
for &(s, radix) in bad_strings.iter() {
assert!(
Float::parse_radix(s, radix).is_err(),
"{} parsed correctly",
s
);
}
let good_strings = [
("INF", 10, Cmp::inf(false)),
("- @iNf@", 16, Cmp::inf(true)),
("+0e99", 2, Cmp::F64(0.0)),
("-9.9e1", 10, Cmp::F64(-99.0)),
("-.99e+2", 10, Cmp::F64(-99.0)),
("+99.e+0", 10, Cmp::F64(99.0)),
("-99@-1", 10, Cmp::F64(-9.9f64)),
("-a_b__.C_d_E_@3", 16, Cmp::F64(f64::from(-0xabcde))),
("1e1023", 2, Cmp::F64(2.0f64.powi(1023))),
(" NaN() ", 10, Cmp::Nan(false)),
(" + NaN (20 Number_Is) ", 10, Cmp::Nan(false)),
(" - @nan@", 2, Cmp::Nan(true)),
];
for &(s, radix, f) in good_strings.iter() {
match Float::parse_radix(s, radix) {
Ok(ok) => assert_eq!(Float::with_val(53, ok), f),
Err(_err) => panic!("could not parse {}", s),
}
}
}
#[test]
fn check_clamping() {
let mut f = Float::new(4);
f.assign(-1);
let dir = f.clamp_round(&1.00002, &1.00001, Round::Down);
assert_eq!(f, 1.0);
assert_eq!(dir, Ordering::Less);
f.assign(-1);
let dir = f.clamp_round(&1.00002, &1.00001, Round::Up);
assert_eq!(f, 1.125);
assert_eq!(dir, Ordering::Greater);
f.assign(2);
let dir = f.clamp_round(&1.00002, &1.00001, Round::Down);
assert_eq!(f, 1.0);
assert_eq!(dir, Ordering::Less);
f.assign(2);
let dir = f.clamp_round(&1.00002, &1.00001, Round::Up);
assert_eq!(f, 1.125);
assert_eq!(dir, Ordering::Greater);
}
#[test]
#[should_panic(expected = "minimum larger than maximum")]
fn check_clamping_panic() {
let mut f = Float::new(4);
f.assign(-1);
f.clamp(&1.00001, &0.99999);
}
#[test]
fn check_formatting() {
let mut f = Float::with_val(53, Special::Zero);
assert_eq!(format!("{}", f), "0.0");
assert_eq!(format!("{:?}", f), "0.0");
assert_eq!(format!("{:+?}", f), "+0.0");
f.assign(Special::NegZero);
assert_eq!(format!("{}", f), "-0.0");
assert_eq!(format!("{:?}", f), "-0.0");
assert_eq!(format!("{:+?}", f), "-0.0");
f.assign(Special::Infinity);
assert_eq!(format!("{}", f), "inf");
assert_eq!(format!("{:+}", f), "+inf");
assert_eq!(format!("{:x}", f), "@inf@");
f.assign(Special::NegInfinity);
assert_eq!(format!("{}", f), "-inf");
assert_eq!(format!("{:x}", f), "-@inf@");
f.assign(Special::Nan);
assert_eq!(format!("{}", f), "NaN");
assert_eq!(format!("{:+}", f), "+NaN");
assert_eq!(format!("{:x}", f), "@NaN@");
f = -f;
assert_eq!(format!("{}", f), "-NaN");
assert_eq!(format!("{:x}", f), "-@NaN@");
f.assign(-27);
assert_eq!(format!("{:.2}", f), "-2.7e1");
assert_eq!(format!("{:.4?}", f), "-2.700e1");
assert_eq!(format!("{:.4e}", f), "-2.700e1");
assert_eq!(format!("{:.4E}", f), "-2.700E1");
assert_eq!(format!("{:.8b}", f), "-1.1011000e4");
assert_eq!(format!("{:.3b}", f), "-1.11e4");
assert_eq!(format!("{:#.8b}", f), "-0b1.1011000e4");
assert_eq!(format!("{:.2o}", f), "-3.3e1");
assert_eq!(format!("{:#.2o}", f), "-0o3.3e1");
assert_eq!(format!("{:.2x}", f), "-1.b@1");
assert_eq!(format!("{:.2X}", f), "-1.B@1");
assert_eq!(format!("{:12.1x}", f), " -1.b@1");
assert_eq!(format!("{:012.3X}", f), "-000001.B0@1");
assert_eq!(format!("{:#012.2x}", f), "-0x00001.b@1");
assert_eq!(format!("{:#12.2X}", f), " -0x1.B@1");
}
#[test]
fn check_assumptions() {
assert_eq!(unsafe { mpfr::custom_get_size(64) }, 8);
assert!(unsafe { mpfr::custom_get_size(32) } <= gmp::NUMB_BITS as usize);
}
#[cfg(feature = "rand")]
struct OnesZerosRand {
one_words: u32,
}
#[cfg(feature = "rand")]
impl RandGen for OnesZerosRand {
fn gen(&mut self) -> u32 {
if self.one_words > 0 {
self.one_words -= 1;
!0
} else {
0
}
}
}
#[cfg(feature = "rand")]
#[test]
fn check_nan_random_bits() {
for i in 0..2 {
let mut zeros_ones = OnesZerosRand { one_words: 2 };
let mut rand = RandState::new_custom(&mut zeros_ones);
let save_emin;
unsafe {
save_emin = mpfr::get_emin();
mpfr::set_emin(-192 + i);
}
let f = Float::with_val(256, Float::random_bits(&mut rand));
if i == 0 {
assert_eq!(f, Float::with_val(64, !0u64) >> 256);
} else {
assert!(f.is_nan());
}
unsafe {
mpfr::set_emin(save_emin);
}
}
}
}