use crate::RoundingMode;
use crate::float::Float;
impl Float {
fn log_taylor(x: &Self) -> Self {
use RoundingMode::None as rm;
let sem = x.get_semantics();
let one = Self::one(sem, false);
let up = Float::sub_with_rm(x, &one, rm);
let down = Float::add_with_rm(x, &one, rm);
let z = Float::div_with_rm(&up, &down, rm);
let z2 = z.sqr();
let mut top = z;
let mut sum = Self::zero(sem, false);
let mut prev = Self::one(sem, true);
for i in 0..50 {
if prev == sum {
break; }
prev = sum.clone();
let bottom = &Self::from_u64(sem, i * 2 + 1);
let elem = Float::div_with_rm(&top, bottom, rm);
sum = Float::add_with_rm(&sum, &elem, rm);
top = Float::mul_with_rm(&top, &z2, rm);
}
sum.scale(1, RoundingMode::Zero)
}
fn log_range_reduce(x: &Self) -> Self {
use RoundingMode::NearestTiesToEven as even;
let sem = x.get_semantics();
let up = Self::from_f64(1.001).cast(sem);
let one = Self::from_u64(sem, 1);
if x > &up {
let sx = x.sqrt();
return Self::log_range_reduce(&sx).scale(1, even);
}
if x < &one {
let re = Float::div_with_rm(&one, x, RoundingMode::None);
return Self::log_range_reduce(&re).neg();
}
Self::log_taylor(x)
}
pub fn log(&self) -> Self {
use RoundingMode::None as rm;
let sem = self.get_semantics();
if !self.is_normal() || self.is_negative() {
return Self::nan(sem, self.get_sign());
}
let orig_sem = self.get_semantics();
let sem = orig_sem.grow_log(10).increase_exponent(10);
let x = &self.cast_with_rm(sem, rm);
Self::log_range_reduce(x).cast_with_rm(orig_sem, rm)
}
}
#[test]
fn test_log() {
use crate::FP128;
let x = Float::from_f64(0.1).cast(FP128).log();
assert_eq!(x.as_f64(), -2.3025850929940455);
for x in [
0.1, 0.5, 2.3, 4.5, 9.8, 11.2, 15.2, 91.2, 102.2, 192.4, 1024.2,
90210.2,
] {
let lhs = Float::from_f64(x).cast(FP128).log().as_f64();
let rhs = x.ln();
assert_eq!(lhs, rhs);
}
}
impl Float {
fn exp_taylor(x: &Self) -> Self {
let sem = x.get_semantics();
use crate::bigint::BigInt;
let mut top = Self::one(sem, false);
let mut bottom = BigInt::one();
let mut sum = Self::zero(sem, false);
let mut prev = Self::one(sem, true);
for k in 1..50 {
if prev == sum {
break; }
prev = sum.clone();
let elem = &top / &Self::from_bigint(sem, bottom.clone());
sum += elem;
bottom *= BigInt::from_u64(k);
top = &top * x;
}
sum
}
fn exp_range_reduce(x: &Self) -> Self {
let sem = x.get_semantics();
let one = Self::from_u64(sem, 1);
if x > &one {
let sx = x.scale(-3, RoundingMode::Zero);
let esx = Self::exp_range_reduce(&sx);
return esx.sqr().sqr().sqr();
}
Self::exp_taylor(x)
}
pub fn exp(&self) -> Self {
let sem = self.get_semantics();
if self.is_zero() {
return Self::one(sem, false);
} else if !self.is_normal() {
return Self::nan(sem, self.get_sign());
}
let orig_sem = self.get_semantics();
let sem = orig_sem.grow_log(10).increase_exponent(10);
if self.is_negative() {
let one = Self::one(sem, false);
return (one / self.cast(sem).neg().exp()).cast(orig_sem);
}
Self::exp_range_reduce(&self.cast(sem)).cast(orig_sem)
}
}
#[test]
fn test_exp() {
assert_eq!(Float::from_f64(2.51).exp().as_f64(), 12.30493006051041);
for x in [
0.000003, 0.001, 0.12, 0.13, 0.5, 1.2, 2.3, 4.5, 9.8, 5.0, 11.2, 15.2,
25.0, 34.001, 54., 89.1, 91.2, 102.2, 150., 192.4, 212., 256., 102.3,
] {
let lhs = Float::from_f64(x).exp().as_f64();
let rhs = x.exp();
assert_eq!(lhs, rhs);
}
}
impl Float {
pub fn sigmoid(&self) -> Self {
let one = Self::one(self.get_semantics(), false);
if self.is_inf() {
return Self::one(self.get_semantics(), self.get_sign());
} else if self.is_zero() {
use RoundingMode::Zero as rm;
return one.scale(-1, rm);
} else if self.is_nan() {
return self.clone();
}
let ex = self.exp();
&ex / (&ex + &one)
}
}
#[test]
pub fn test_sigmoid() {
let inp = [-0.5, 0., 0.5, 0.99, 1., 2.3, 100.];
let out = [
0.37754067, 0.5, 0.62245933, 0.72908792, 0.73105858, 0.90887704, 1.,
];
for (x, o) in inp.iter().zip(out.iter()) {
let x = Float::from_f64(*x);
let o = Float::from_f64(*o);
let res = x.sigmoid();
assert_eq!(o.as_f32(), res.as_f32())
}
}