1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
use super::*;
use polynomial_ops::*;
use array__ops::*;
#[const_trait]
pub trait ApproxSin
{
/// Calculates an approximation of a sine, using the Z80 / ZX Spectrum algorithm.
///
/// https://namoseley.wordpress.com/2012/09/26/arduinoavr-and-zx-spectrum-sin-routines/
///
/// This approximation is significantly slower than the built-in sin method in the standard library,
/// however this method can be run at compile-time.
///
/// # Example
///
/// ```rust
/// #![feature(const_trait_impl)]
///
/// use float_approx_math::ApproxSin;
///
/// const X: f32 = 2.0;
/// let y: f32 = X.approx_sin();
///
/// assert!((y - X.sin()).abs() < 0.0000005); // Less than 0.0000005 abs error
/// ```
fn approx_sin(self) -> Self;
}
macro_rules! impl_approx_sin {
($float:ty; $consts:tt) => {
impl /*const*/ ApproxSin for $float
{
fn approx_sin(self) -> Self
{
const N: usize = 6;
const C: [$float; N] = [
1.276278962,
-0.285261569,
0.009118016,
-0.000136587,
0.000001185,
-0.000000007
];
let t: [[$float; N]; N] = ArrayOps::fill(
/*const*/ |n| Into::<Option<[$float; N]>>::into(ChebyshevPolynomial::new_of_first_kind(n)).unwrap()
);
let p: [$float; N] = t.zip(C)
.map2(/*const*/ |(t, c)| t.map2(const |tn| c*tn))
.reduce(/*const*/ |a, b| a.zip(b).map2(const |(a, b)| a + b))
.unwrap_or_default();
let mut w = self*$consts::FRAC_2_PI;
let mut i = 0;
while i < 4
{
w = (w - 1.0);
if i % 2 == 0 && w < 0.0
{
w = -w;
}
w %= 4.0;
i += 1;
}
let w = if w > 1.0 {2.0 - w} else if w < -1.0 {-2.0 - w} else {w};
let z = 2.0*w*w - 1.0;
p.evaluate_as_polynomial(z)*w
}
}
};
}
impl_approx_sin!(f32; f32);
impl_approx_sin!(f64; f64);
#[cfg(test)]
mod test
{
use ::test::Bencher;
use super::*;
use crate::tests as t;
#[test]
fn sin()
{
const RANGE: f32 = f32::TAU;
t::plot_approx("sin", -RANGE..RANGE, f32::sin, ApproxSin::approx_sin)
}
#[bench]
fn sin_benchmark(_: &mut Bencher)
{
type F = f64;
const N: usize = 500;
const S: usize = 32;
t::plot_benchmark::<_, _, N, _>(
"sin",
[
&F::sin,
&ApproxSin::approx_sin
],
-f64::TAU..f64::TAU,
S
)
}
}