use rug::Integer;
use std::cmp::Ordering;
use mpmfnum::rfloat::{RFloat, RFloatContext};
use mpmfnum::{Real, RoundingContext, RoundingMode, Split};
#[test]
fn traits() {
assert_eq!(RFloat::radix(), 2, "RFloat is a binary format");
let vals = [
RFloat::zero(), RFloat::one(), RFloat::Real(true, -4, Integer::from(7)), RFloat::PosInfinity, RFloat::NegInfinity, RFloat::Nan, ];
let expected = [
Some(false),
Some(false),
Some(true),
Some(false),
Some(true),
None,
];
for (val, &expected) in vals.iter().zip(expected.iter()) {
let actual = val.sign();
assert_eq!(
actual, expected,
"{:?} has unexpected sign; expected {:?}, actual {:?}",
val, expected, actual
);
}
let expected = [None, Some(0), Some(-4), None, None, None];
for (val, &expected) in vals.iter().zip(expected.iter()) {
let actual = val.exp();
assert_eq!(
actual, expected,
"{:?} has unexpected exponent (exp); expected {:?}, actual {:?}",
val, expected, actual
);
}
let expected = [None, Some(0), Some(-2), None, None, None];
for (val, &expected) in vals.iter().zip(expected.iter()) {
let actual = val.e();
assert_eq!(
actual, expected,
"{:?} has unexpected exponent (e); expected {:?}, actual {:?}",
val, expected, actual
);
}
let expected = [None, Some(-1), Some(-5), None, None, None];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.n();
assert_eq!(
actual,
expected.clone(),
"{:?} has unexpected least significant exponent (n); expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [
Some(Integer::from(0)),
Some(Integer::from(1)),
Some(Integer::from(7)),
None,
None,
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.c();
assert_eq!(
actual,
expected.clone(),
"{:?} has unexpected significand (c): expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [
Some(Integer::from(0)),
Some(Integer::from(1)),
Some(Integer::from(-7)),
None,
None,
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.m();
assert_eq!(
actual,
expected.clone(),
"{:?} has unexpected significand (m): expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [Some(0), Some(1), Some(3), None, None, None];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.prec();
assert_eq!(
actual,
expected.clone(),
"{:?} has unexpected precision (p): expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [false, false, false, true, true, true];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.is_nar();
assert_eq!(
actual,
expected.clone(),
"{:?} is unexpectedly not-a-real (NAR): expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [true, true, true, false, false, false];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.is_finite();
assert_eq!(
actual,
expected.clone(),
"{:?} is unexpectedly finite: expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [false, false, false, true, true, false];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.is_infinite();
assert_eq!(
actual,
expected.clone(),
"{:?} is unexpectedly infinite: expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [true, false, false, false, false, false];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.is_zero();
assert_eq!(
actual,
expected.clone(),
"{:?} is unexpectedly zero: expected {:?}, actual {:?}",
val,
expected,
actual
);
}
let expected = [None, Some(false), Some(true), Some(false), Some(true), None];
for (val, expected) in vals.iter().zip(expected.iter()) {
let actual = val.is_negative();
assert_eq!(
actual,
expected.clone(),
"{:?} is unexpectedly zero: expected {:?}, actual {:?}",
val,
expected,
actual
);
}
}
#[test]
fn round_trivial() {
let ctx = RFloatContext::new().with_max_p(1);
let zero = RFloat::zero();
let (p, n) = ctx.round_params(&zero);
let split = Split::new(&zero, p, n);
let err = split.lost();
let rounded = ctx.round(&zero);
assert!(rounded.is_zero(), "round(0) = 0");
assert!(err.is_zero(), "rounding 0 should have a zero lost bits");
let rounded = ctx.round(&RFloat::PosInfinity);
assert!(rounded.is_infinite(), "round(+Inf) = +Inf");
let rounded = ctx.round(&RFloat::NegInfinity);
assert!(rounded.is_infinite(), "round(-Inf) = -Inf");
let rounded = ctx.round(&RFloat::Nan);
assert!(rounded.is_nar(), "round(-Nan) = Nan");
}
fn round1(ctx: &RFloatContext, num: &RFloat) -> (RFloat, RFloat) {
let (p, n) = ctx.round_params(num);
let split = Split::new(num, p, n);
let err = split.lost().clone();
let rounded = ctx.round(num);
(rounded, err)
}
#[test]
fn round_fixed() {
let one_3_4 = RFloat::Real(false, -2, Integer::from(7));
let one_1_2 = RFloat::Real(false, -1, Integer::from(3));
let one = RFloat::one();
let three_4 = RFloat::Real(false, -2, Integer::from(3));
let one_4 = RFloat::Real(false, -2, Integer::from(1));
let zero = RFloat::zero();
let neg_one = RFloat::Real(true, 0, Integer::from(1));
let ctx = RFloatContext::new()
.with_min_n(-1)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &one);
assert_eq!(rounded, RFloat::one(), "rounding should not have lost bits");
assert!(err.is_zero(), "lost bits should be 0");
let ctx = RFloatContext::new()
.with_min_n(0)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &one);
assert_eq!(rounded, RFloat::zero(), "rounding should be truncated to 0");
assert_eq!(err, RFloat::one(), "lost bits should be 1");
let ctx = RFloatContext::new()
.with_min_n(0)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &neg_one);
assert_eq!(rounded, zero, "rounding should truncated to 0");
assert_eq!(err, neg_one, "lost bits should be -1");
let ctx = RFloatContext::new()
.with_min_n(-1)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &one_3_4);
assert_eq!(rounded, one, "rounding should truncated to 0");
assert_eq!(err, three_4, "lost bits should be 3/4");
let ctx = RFloatContext::new()
.with_min_n(-2)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &one_3_4);
assert_eq!(rounded, one_1_2, "rounding should truncated to 0");
assert_eq!(err, one_4, "lost bits should be 1/4");
let ctx = RFloatContext::new()
.with_min_n(10)
.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &one);
assert_eq!(rounded, zero, "rounding should truncated to 0");
assert_eq!(err, one, "lost bits should be 1");
}
#[test]
fn round_float() {
let one_1_2 = RFloat::Real(false, -1, Integer::from(3));
let one_1_4 = RFloat::Real(false, -2, Integer::from(5));
let one_1_8 = RFloat::Real(false, -3, Integer::from(9));
let one = RFloat::one();
let one_4 = RFloat::Real(false, -2, Integer::from(1));
let one_8 = RFloat::Real(false, -3, Integer::from(1));
let zero = RFloat::zero();
let ctx = RFloatContext::new().with_max_p(3);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one_1_4, "rounding should be exact");
assert_eq!(err, zero, "lost bits is zero");
let ctx = ctx.with_max_p(2);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToPositive);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one_1_2, "rounding goes to 3/2");
assert_eq!(err, one_4, "lost bits is -1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToNegative);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToEven);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToOdd);
let (rounded, err) = round1(&ctx, &one_1_4);
assert_eq!(rounded, one_1_2, "rounding goes to 3/2");
assert_eq!(err, one_4, "lost bits is -1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::NearestTiesToEven);
let (rounded, err) = round1(&ctx, &one_1_8);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::ToPositive);
let (rounded, err) = round1(&ctx, &one_1_8);
assert_eq!(rounded, one_1_2, "rounding goes to 3/2");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::ToNegative);
let (rounded, err) = round1(&ctx, &one_1_8);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::ToEven);
let (rounded, err) = round1(&ctx, &one_1_8);
assert_eq!(rounded, one, "rounding goes to 1");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::ToOdd);
let (rounded, err) = round1(&ctx, &one_1_8);
assert_eq!(rounded, one_1_2, "rounding goes to 3/2");
assert_eq!(err, one_8, "lost bits is -3/8");
}
#[test]
fn round_float_subnorm() {
let one = RFloat::one();
let half_way = RFloat::Real(false, -3, Integer::from(7));
let tiny_val = RFloat::Real(false, -2, Integer::from(3));
let one_2 = RFloat::Real(false, -1, Integer::from(1));
let one_4 = RFloat::Real(false, -2, Integer::from(1));
let one_8 = RFloat::Real(false, -3, Integer::from(1));
let ctx = RFloatContext::new().with_max_p(2);
let (rounded, err) = round1(&ctx, &half_way);
assert_eq!(one, rounded, "rounding to 1");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::AwayZero);
let (rounded, err) = round1(&ctx, &half_way);
assert_eq!(one, rounded, "rounding to 1");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = ctx.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &half_way);
assert_eq!(tiny_val, rounded, "rounding to 3/4");
assert_eq!(err, one_8, "lost bits is 1/8");
let ctx = RFloatContext::new().with_max_p(2).with_min_n(-2);
let (rounded, err) = round1(&ctx, &tiny_val);
assert_eq!(one, rounded, "rounding to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::AwayZero);
let (rounded, err) = round1(&ctx, &tiny_val);
assert_eq!(one, rounded, "rounding to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToZero);
let (rounded, err) = round1(&ctx, &tiny_val);
assert_eq!(one_2, rounded, "rounding to 1/2");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToEven);
let (rounded, err) = round1(&ctx, &tiny_val);
assert_eq!(one, rounded, "rounding to 1");
assert_eq!(err, one_4, "lost bits is 1/4");
let ctx = ctx.with_rounding_mode(RoundingMode::ToOdd);
let (rounded, err) = round1(&ctx, &tiny_val);
assert_eq!(one_2, rounded, "rounding to 1/2");
assert_eq!(err, one_4, "lost bits is 1/4");
}
fn assert_expected_cmp(x: &RFloat, y: &RFloat, expected: &Option<Ordering>) {
let actual = x.partial_cmp(y);
assert_eq!(
actual,
expected.clone(),
"unexpected comparison result between {:?} and {:?}: expected {:?}, actual {:?}",
x,
y,
expected,
actual
);
}
#[test]
fn ordering() {
let vals = [
RFloat::zero(),
RFloat::one(),
RFloat::PosInfinity.clone(),
RFloat::NegInfinity.clone(),
RFloat::Nan.clone(),
];
let zero = RFloat::zero();
let expected = [
Some(Ordering::Equal),
Some(Ordering::Less),
Some(Ordering::Less),
Some(Ordering::Greater),
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
assert_expected_cmp(&zero, val, expected);
}
let one = RFloat::one();
let expected = [
Some(Ordering::Greater),
Some(Ordering::Equal),
Some(Ordering::Less),
Some(Ordering::Greater),
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
assert_expected_cmp(&one, val, expected);
}
let expected = [
Some(Ordering::Greater),
Some(Ordering::Greater),
Some(Ordering::Equal),
Some(Ordering::Greater),
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
assert_expected_cmp(&RFloat::PosInfinity, val, expected);
}
let expected = [
Some(Ordering::Less),
Some(Ordering::Less),
Some(Ordering::Less),
Some(Ordering::Equal),
None,
];
for (val, expected) in vals.iter().zip(expected.iter()) {
assert_expected_cmp(&RFloat::NegInfinity, val, expected);
}
let expected = [None, None, None, None, None];
for (val, expected) in vals.iter().zip(expected.iter()) {
assert_expected_cmp(&RFloat::Nan, val, expected);
}
let one = RFloat::one();
let also_one = RFloat::Real(false, -1, Integer::from(2));
assert_eq!(
one.partial_cmp(&also_one),
Some(Ordering::Equal),
"should be the same"
);
let still_one = RFloat::Real(false, -2, Integer::from(4));
assert_eq!(
one.partial_cmp(&still_one),
Some(Ordering::Equal),
"should be the same"
);
}
fn is_equal(x: &RFloat, y: &RFloat) -> bool {
match (x, y) {
(RFloat::Nan, RFloat::Nan) => true,
(_, _) => *x == *y,
}
}
fn assert_expected_mul(x: &RFloat, y: &RFloat, expected: &RFloat) {
let left = x.clone() * y.clone();
let right = y.clone() * x.clone();
assert!(
is_equal(&left, expected),
"for {:?} * {:?}: expected {:?}, actual {:?}",
x,
y,
expected,
left
);
assert!(
is_equal(&left, expected),
"multiplication is commutative: {:?} != {:?}",
left,
right
);
}
#[test]
fn multiplication() {
let zero = RFloat::zero(); let one = RFloat::one(); let frac = RFloat::Real(true, -4, Integer::from(7)); let pos_inf = RFloat::PosInfinity; let neg_inf = RFloat::NegInfinity; let nan = RFloat::Nan;
let vals = [&zero, &one, &frac, &pos_inf, &neg_inf, &nan];
let expected = [&zero, &zero, &zero, &nan, &nan, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&zero, val, expected);
}
let expected = [&zero, &one, &frac, &pos_inf, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&one, val, expected);
}
let frac_sqr = RFloat::Real(false, -8, Integer::from(49));
let expected = [&zero, &frac, &frac_sqr, &neg_inf, &pos_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&frac, val, expected);
}
let expected = [&nan, &pos_inf, &neg_inf, &pos_inf, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&pos_inf, val, expected);
}
let expected = [&nan, &neg_inf, &pos_inf, &neg_inf, &pos_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&neg_inf, val, expected);
}
let expected = [&nan; 6];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_mul(&nan, val, expected);
}
}
fn assert_expected_add(x: &RFloat, y: &RFloat, expected: &RFloat) {
let left = x.clone() + y.clone();
let right = y.clone() + x.clone();
assert!(
is_equal(&left, expected),
"for {:?} + {:?}: expected {:?}, actual {:?}",
x,
y,
expected,
left
);
assert!(
is_equal(&left, expected),
"addition is commutative: {:?} != {:?}",
left,
right
);
}
#[test]
fn addition() {
let zero = RFloat::zero(); let one = RFloat::one(); let frac = RFloat::Real(true, -4, Integer::from(7)); let pos_inf = RFloat::PosInfinity; let neg_inf = RFloat::NegInfinity; let nan = RFloat::Nan;
let two = RFloat::Real(false, 0, Integer::from(2)); let two_frac = RFloat::Real(true, -4, Integer::from(14)); let one_m_frac = RFloat::Real(false, -4, Integer::from(9));
let vals = [&zero, &one, &frac, &pos_inf, &neg_inf, &nan];
let expected = [&zero, &one, &frac, &pos_inf, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&zero, val, expected);
}
let expected = [&one, &two, &one_m_frac, &pos_inf, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&one, val, expected);
}
let expected = [&frac, &one_m_frac, &two_frac, &pos_inf, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&frac, val, expected);
}
let expected = [&pos_inf, &pos_inf, &pos_inf, &pos_inf, &nan, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&pos_inf, val, expected);
}
let expected = [&neg_inf, &neg_inf, &neg_inf, &nan, &neg_inf, &nan];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&neg_inf, val, expected);
}
let expected = [&nan; 6];
for (&val, &expected) in vals.iter().zip(expected.iter()) {
assert_expected_add(&nan, val, expected);
}
}
#[test]
fn neg() {
let zero = RFloat::zero(); let one = RFloat::one(); let frac = RFloat::Real(true, -4, Integer::from(7)); let pos_inf = RFloat::PosInfinity; let neg_inf = RFloat::NegInfinity;
let neg_zero = -zero;
let neg_one = -one;
let neg_frac = -frac;
let neg_pos_inf = -pos_inf;
let neg_neg_inf = -neg_inf;
assert!(!neg_zero.sign().unwrap(), "-0 should not have a sign");
assert!(neg_one.sign().unwrap(), "-1 is signed");
assert!(!neg_frac.sign().unwrap(), "-(-7 * 2^-4) is not signed");
assert!(neg_pos_inf.sign().unwrap(), "-(+Inf) is signed");
assert!(!neg_neg_inf.sign().unwrap(), "-(-Inf) is not signed");
}
#[test]
fn mpfr_integration() {
let zero = RFloat::zero(); let one = RFloat::one(); let frac = RFloat::Real(true, -4, Integer::from(7)); let pos_inf = RFloat::PosInfinity;
let neg_inf = RFloat::NegInfinity;
let nan = RFloat::Nan;
let vals = [zero, one, frac, pos_inf, neg_inf, nan];
for val in &vals {
let f: RFloat = val.clone().into();
let val2 = RFloat::from(f);
assert!(
is_equal(val, &val2),
"conversion should have been exact: {:?} != {:?}",
val,
val2
);
}
}