use crate::float::Float;
use crate::RoundingMode;
impl Float {
fn sin_taylor(x: &Self) -> Self {
use crate::bigint::BigInt;
let sem = x.get_semantics();
let mut neg = false;
let mut top = x.clone();
let mut bottom = BigInt::one();
let mut sum = Self::zero(sem, false);
let x2 = x.sqr();
let mut prev = Self::one(sem, true);
for i in 1..50 {
if prev == sum {
break; }
prev = sum.clone();
let elem = &top / &Self::from_bigint(sem, bottom.clone());
sum = if neg { sum - elem } else { sum + elem };
top = &top * &x2;
let next_term = BigInt::from_u64((i * 2) * (i * 2 + 1));
bottom *= next_term;
neg ^= true;
}
sum
}
fn sin_step4_reduction(x: &Self, steps: usize) -> Self {
use RoundingMode::None as rm;
if steps == 0 {
return Self::sin_taylor(x);
}
let i3 = Float::from_u64(x.get_semantics(), 3);
let x3 = Float::div_with_rm(x, &i3, rm);
let sx = Float::sin_step4_reduction(&x3, steps - 1);
let sx3 = Float::mul_with_rm(&sx, &i3, rm);
Float::sub_with_rm(&sx3, &sx.powi(3).scale(2, rm), rm)
}
pub fn sin(&self) -> Self {
use RoundingMode::None as rm;
if self.is_zero() || self.is_nan() {
return self.clone();
}
if self.is_inf() {
return Self::nan(self.get_semantics(), self.get_sign());
}
let orig_sem = self.get_semantics();
let sem = orig_sem.grow_log(12).increase_exponent(4);
assert!(self.is_normal());
let mut neg = false;
let mut val = self.cast_with_rm(sem, rm);
if val.is_negative() {
val = val.neg();
neg ^= true;
}
let is_small = self.get_exp() < 0;
if !is_small {
let pi = Self::pi(sem);
let pi2 = pi.scale(1, rm);
let pi_half = pi.scale(-1, rm);
if val > pi2 {
val = val.rem(&pi2);
}
debug_assert!(val <= pi2);
if val > pi {
val = Float::sub_with_rm(&val, &pi, rm);
neg ^= true;
}
debug_assert!(val <= pi);
if val > pi_half {
val = Float::sub_with_rm(&pi, &val, rm);
}
debug_assert!(val <= pi_half);
}
let k = orig_sem.log_precision() * 4;
let res = Self::sin_step4_reduction(&val, k);
let res = if neg { res.neg() } else { res };
res.cast(orig_sem)
}
}
#[cfg(feature = "std")]
#[test]
fn test_sin_known_value() {
use crate::std::string::ToString;
let res = Float::from_f64(801. / 10000.).sin().to_string();
assert_eq!(res, ".08001437374006335");
let res = Float::from_f64(90210. / 10000.).sin().to_string();
assert_eq!(res, ".3928952872542333");
let res = Float::from_f64(95051.).sin().to_string();
assert_eq!(res, "-.8559198239971502");
}
#[cfg(feature = "std")]
#[test]
fn test_sin() {
use crate::utils;
for i in -100..100 {
let f0 = i as f64;
let r0 = f0.sin();
let r1 = Float::from_f64(f0).sin().as_f64();
assert_eq!(r0, r1);
}
for i in -300..300 {
let f0 = (i as f64) / 100.;
let r0 = f0.sin();
let r1 = Float::from_f64(f0).sin().as_f64();
assert_eq!(r0, r1);
}
for v in utils::get_special_test_values() {
if v.is_normal() {
continue;
}
let r0 = v.sin();
let r1 = Float::from_f64(v).sin().as_f64();
assert_eq!(r0.is_nan(), r1.is_nan());
if !r0.is_nan() {
assert_eq!(r0, r1);
}
}
}
impl Float {
fn cos_taylor(x: &Self) -> Self {
use crate::bigint::BigInt;
let sem = x.get_semantics();
let mut neg = false;
let mut top = Self::one(sem, false);
let mut bottom = BigInt::one();
let mut sum = Self::zero(sem, false);
let x2 = x.sqr();
let mut prev = Self::one(sem, true);
for i in 1..50 {
if prev == sum {
break; }
prev = sum.clone();
let elem = &top / &Self::from_bigint(sem, bottom.clone());
sum = if neg { sum - elem } else { sum + elem };
top = &top * &x2;
let next_term = BigInt::from_u64((i * 2 - 1) * (i * 2));
bottom *= next_term;
neg ^= true;
}
sum
}
fn cos_step4_reduction(x: &Self, steps: usize) -> Self {
use RoundingMode::None as rm;
if steps == 0 {
return Self::cos_taylor(x);
}
let sem = x.get_semantics();
let one = Float::one(sem, false);
let half_x = x.scale(-1, rm);
let sx = Float::cos_step4_reduction(&half_x, steps - 1);
Float::sub_with_rm(&sx.sqr().scale(1, rm), &one, rm)
}
pub fn cos(&self) -> Self {
use RoundingMode::None as rm;
if self.is_nan() {
return self.clone();
}
if self.is_zero() {
return Self::one(self.get_semantics(), false);
}
if self.is_inf() {
return Self::nan(self.get_semantics(), self.get_sign());
}
let orig_sem = self.get_semantics();
let sem = orig_sem.grow_log(14).increase_exponent(4);
assert!(self.is_normal());
let mut neg = false;
let mut val = self.cast_with_rm(sem, rm);
if val.is_negative() {
val = val.neg();
}
let is_small = self.get_exp() < 0;
if !is_small {
let pi = Self::pi(sem);
let pi2 = pi.scale(1, rm);
let pi_half = pi.scale(-1, rm);
if val > pi2 {
val = val.rem(&pi2);
}
debug_assert!(val <= pi2);
if val > pi {
val = Float::sub_with_rm(&pi2, &val, rm);
}
debug_assert!(val <= pi);
if val > pi_half {
val = Float::sub_with_rm(&pi, &val, rm);
neg ^= true;
}
debug_assert!(val <= pi_half);
}
let k = (sem.log_precision() * 8) / 10;
let res = Self::cos_step4_reduction(&val, k);
let res = if neg { res.neg() } else { res };
res.cast(orig_sem)
}
}
#[cfg(feature = "std")]
#[test]
fn test_cos_known_value() {
use crate::std::string::ToString;
let res = Float::from_f64(801. / 10000.).cos().to_string();
assert_eq!(res, ".9967937098492272");
let res = Float::from_f64(2.3).cos().to_string();
assert_eq!(res, "-.6662760212798241");
let res = Float::from_f64(90210. / 10000.).cos().to_string();
assert_eq!(res, "-.9195832171442742");
let res = Float::from_f64(95051.).cos().to_string();
assert_eq!(res, ".5171085523259959");
}
#[cfg(feature = "std")]
#[test]
fn test_cos() {
use crate::utils;
for i in -100..100 {
let f0 = i as f64;
let r0 = f0.cos();
let r1 = Float::from_f64(f0).cos().as_f64();
assert_eq!(r0, r1);
}
for i in -100..100 {
let f0 = (i as f64) / 100.;
let r0 = f0.cos();
let r1 = Float::from_f64(f0).cos().as_f64();
assert_eq!(r0, r1);
}
for v in utils::get_special_test_values() {
if v.is_normal() {
continue;
}
let r0 = v.cos();
let r1 = Float::from_f64(v).cos().as_f64();
assert_eq!(r0.is_nan(), r1.is_nan());
if !r0.is_nan() {
assert_eq!(r0, r1);
}
}
}
impl Float {
pub fn tan(&self) -> Self {
use RoundingMode::None as rm;
if self.is_zero() || self.is_nan() {
return self.clone();
}
if self.is_inf() {
return Self::nan(self.get_semantics(), self.get_sign());
}
let orig_sem = self.get_semantics();
let sem = orig_sem.grow_log(12).increase_exponent(4);
assert!(self.is_normal());
let mut neg = false;
let mut val = self.cast_with_rm(sem, rm);
if val.is_negative() {
val = val.neg();
neg ^= true;
}
let is_small = self.get_exp() < 0;
if !is_small {
let pi = Self::pi(sem);
let half_pi = pi.scale(-1, rm);
if val > pi {
val = val.rem(&pi);
}
debug_assert!(val <= pi);
if val > half_pi {
val = pi - val;
neg ^= true;
}
debug_assert!(val <= half_pi);
}
let sinx = val.sin();
let one = Float::one(sem, false);
let bottom = (one - sinx.sqr()).sqrt();
let res = sinx / bottom;
let res = if neg { res.neg() } else { res };
res.cast(orig_sem)
}
}
#[cfg(feature = "std")]
#[test]
fn test_tan_known_value() {
use crate::std::string::ToString;
let res = Float::from_f64(801. / 10000.).tan().to_string();
assert_eq!(res, ".08027174825588148");
let res = Float::from_f64(2.3).tan().to_string();
assert_eq!(res, "-1.1192136417341325");
let res = Float::from_f64(90210. / 10000.).tan().to_string();
assert_eq!(res, "-.4272536513599634");
let res = Float::from_f64(95051.).tan().to_string();
assert_eq!(res, "-1.6552033806966715");
}