use crate::RoundingMode;
use crate::float::Float;
impl Float {
pub fn powi(&self, mut n: u64) -> Self {
let sem = self.get_semantics().increase_precision(2);
let mut elem = Self::one(sem, false);
let mut val = self.cast(sem);
while n > 0 {
if n & 1 == 1 {
elem *= &val;
}
val *= &val.clone();
n >>= 1;
}
elem.cast(self.get_semantics())
}
pub fn sqr(&self) -> Self {
self.powi(2)
}
pub fn sqrt(&self) -> Self {
let sem = self.get_semantics();
if self.is_zero() {
return self.clone(); } else if self.is_nan() || self.is_negative() {
return Self::nan(sem, self.get_sign()); } else if self.is_inf() {
return self.clone(); }
let target = self.clone();
let two = Self::from_u64(sem, 2);
let mut x = if target < two { two } else { target.clone() };
let mut prev = x.clone();
loop {
x += &target / &x;
x = x.scale(-1, RoundingMode::NearestTiesToEven);
if prev < x || x == prev {
return x;
}
prev = x.clone();
}
}
pub fn abs(&self) -> Self {
let mut x = self.clone();
x.set_sign(false);
x
}
pub fn max(&self, other: &Self) -> Self {
if self.is_nan() {
return other.clone();
} else if other.is_nan() {
return self.clone();
} else if self.get_sign() != other.get_sign() {
return if self.get_sign() {
other.clone()
} else {
self.clone()
}; }
if self > other {
self.clone()
} else {
other.clone()
}
}
pub fn min(&self, other: &Self) -> Self {
if self.is_nan() {
return other.clone();
} else if other.is_nan() {
return self.clone();
} else if self.get_sign() != other.get_sign() {
return if self.get_sign() {
self.clone()
} else {
other.clone()
}; }
if self > other {
other.clone()
} else {
self.clone()
}
}
}
#[cfg(feature = "std")]
#[test]
fn test_sqrt() {
use crate::utils;
use crate::FP64;
for i in 0..256 {
let v16 = Float::from_u64(FP64, i * i);
assert_eq!(v16.sqrt().as_f64(), (i) as f64);
}
for v_f64 in utils::get_special_test_values() {
let vf = Float::from_f64(v_f64);
assert_eq!(vf.sqrt().is_inf(), v_f64.sqrt().is_infinite());
assert_eq!(vf.sqrt().is_nan(), v_f64.sqrt().is_nan());
assert_eq!(vf.sqrt().is_negative(), v_f64.sqrt().is_sign_negative());
}
fn check(inp: f64, res: f64) {
assert_eq!(Float::from_f64(inp).sqrt().as_f64(), res);
}
check(1.5, 1.224744871391589);
check(2.3, 1.51657508881031);
check(6.7, 2.588435821108957);
check(7.9, 2.8106938645110393);
check(11.45, 3.383784863137726);
check(1049.3, 32.39290045673589);
check(90210.7, 300.35096137685326);
check(199120056003.73413, 446228.70369770494);
check(0.6666666666666666, 0.816496580927726);
check(0.4347826086956522, 0.6593804733957871);
check(0.14925373134328357, 0.3863337046431279);
check(0.12658227848101264, 0.35578403348241);
check(0.08733624454148473, 0.29552706228277087);
check(0.0009530162965786716, 0.030870962028719993);
check(1.1085159520988087e-5, 0.00332943831914455);
check(5.0120298432056786e-8, 0.0002238756316173263);
}
#[cfg(feature = "std")]
#[test]
fn test_min_max() {
use crate::utils;
fn check(v0: f64, v1: f64) {
let correct = v0.min(v1);
let test = Float::from_f64(v0).min(&Float::from_f64(v1)).as_f64();
assert_eq!(test.is_nan(), correct.is_nan());
if !correct.is_nan() {
assert_eq!(correct, test);
}
let correct = v0.max(v1);
let test = Float::from_f64(v0).max(&Float::from_f64(v1)).as_f64();
assert_eq!(test.is_nan(), correct.is_nan());
if !correct.is_nan() {
assert_eq!(correct, test);
}
}
for v0 in utils::get_special_test_values() {
for v1 in utils::get_special_test_values() {
check(v0, v1);
}
}
let mut lfsr = utils::Lfsr::new();
for _ in 0..100 {
let v0 = f64::from_bits(lfsr.get64());
let v1 = f64::from_bits(lfsr.get64());
check(v0, v1);
}
}
#[cfg(feature = "std")]
#[test]
fn test_abs() {
use crate::utils;
for v in utils::get_special_test_values() {
if !v.is_nan() {
assert_eq!(Float::from_f64(v).abs().as_f64(), v.abs());
}
}
}
impl Float {
pub fn scale(&self, scale: i64, rm: RoundingMode) -> Self {
use crate::bigint::LossFraction;
if !self.is_normal() {
return self.clone();
}
let mut r = Self::from_parts(
self.get_semantics(),
self.get_sign(),
self.get_exp() + scale,
self.get_mantissa(),
);
r.normalize(rm, LossFraction::ExactlyZero);
r
}
pub fn rem(&self, rhs: &Self) -> Self {
use core::ops::Sub;
if self.is_nan() || rhs.is_nan() || self.is_inf() || rhs.is_zero() {
return Self::nan(self.get_semantics(), self.get_sign());
}
if self.is_zero() || rhs.is_inf() {
return self.clone();
}
let mut lhs = self.abs();
let rhs = if rhs.is_negative() {
rhs.neg()
} else {
rhs.clone()
};
debug_assert!(lhs.is_normal() && rhs.is_normal());
while lhs >= rhs && lhs.is_normal() {
let scale = lhs.get_exp() - rhs.get_exp();
let mut diff = rhs.scale(scale, RoundingMode::None);
if diff > lhs {
diff = rhs.scale(scale - 1, RoundingMode::None);
}
lhs = lhs.sub(diff);
}
lhs.set_sign(self.get_sign());
lhs
}
}
#[test]
fn test_scale() {
use crate::FP64;
let x = Float::from_u64(FP64, 1);
let y = x.scale(1, RoundingMode::None);
assert_eq!(y.as_f64(), 2.0);
let z = x.scale(-1, RoundingMode::None);
assert_eq!(z.as_f64(), 0.5);
}
#[cfg(feature = "std")]
#[test]
fn test_rem() {
use crate::utils;
use crate::utils::Lfsr;
use core::ops::Rem;
fn check_two_numbers(v0: f64, v1: f64) {
let f0 = Float::from_f64(v0);
let f1 = Float::from_f64(v1);
let r0 = v0.rem(v1);
let r1 = f0.rem(&f1).as_f64();
assert_eq!(r0.is_nan(), r1.is_nan());
if !r0.is_nan() {
assert_eq!(r0, r1);
}
}
check_two_numbers(1.4, 2.5);
check_two_numbers(2.4, 1.5);
check_two_numbers(1000., std::f64::consts::PI);
check_two_numbers(10000000000000000000., std::f64::consts::PI / 1000.);
check_two_numbers(10000000000000000000., std::f64::consts::PI);
check_two_numbers(100., std::f64::consts::PI);
check_two_numbers(100., -std::f64::consts::PI);
check_two_numbers(0., 10.);
check_two_numbers(std::f64::consts::PI, 10.0);
let mut lfsr = Lfsr::new();
for _ in 0..5000 {
let v0 = f64::from_bits(lfsr.get64());
let v1 = f64::from_bits(lfsr.get64());
check_two_numbers(v0, v1);
}
for v0 in utils::get_special_test_values() {
for v1 in utils::get_special_test_values() {
check_two_numbers(v0, v1);
}
}
}
#[test]
fn test_powi() {
assert_eq!(Float::from_f64(2.).powi(0).as_f64(), 1.);
assert_eq!(Float::from_f64(2.).powi(1).as_f64(), 2.);
assert_eq!(Float::from_f64(2.).powi(3).as_f64(), 8.);
assert_eq!(Float::from_f64(2.).powi(5).as_f64(), 32.);
assert_eq!(Float::from_f64(2.).powi(10).as_f64(), 1024.);
assert_eq!(Float::from_f64(0.3).powi(3).as_f64(), 0.026999999999999996);
}
impl Float {
pub fn pow(&self, n: &Float) -> Self {
let orig_sem = self.get_semantics();
let one = Self::one(orig_sem, false);
let sign = self.get_sign();
assert_eq!(orig_sem, n.get_semantics());
if *self == one {
return self.clone();
} else if n.is_inf() || n.is_nan() {
return Self::nan(orig_sem, sign);
} else if n.is_zero() {
return Self::one(orig_sem, sign);
} else if self.is_zero() {
return if n.is_negative() {
Self::inf(orig_sem, sign)
} else {
Self::zero(orig_sem, sign)
};
} else if self.is_negative() || self.is_inf() || self.is_nan() {
return Self::nan(orig_sem, sign);
}
let sem = orig_sem.grow_log(10).increase_exponent(10);
(n.cast(sem) * self.cast(sem).log()).exp().cast(orig_sem)
}
}
#[test]
fn test_pow() {
fn my_pow(a: f32, b: f32) -> f32 {
Float::from_f32(a).pow(&Float::from_f32(b)).as_f32()
}
assert_eq!(my_pow(1.24, 1.2), 1.2945118);
assert_eq!(my_pow(0.94, 13.), 0.44736509);
assert_eq!(my_pow(0.11, -8.), 46650738.02097334);
assert_eq!(my_pow(40.0, 3.1), 92552.0);
for i in 0..30 {
for j in -10..10 {
let i = i as f64;
let j = j as f64;
let res = i.powf(j);
let res2 = Float::from_f64(i).pow(&Float::from_f64(j));
assert_eq!(res, res2.as_f64());
}
}
}