Skip to main content

trig_const/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![forbid(unsafe_code)]
4#![allow(clippy::excessive_precision)]
5#![allow(clippy::approx_constant)]
6
7mod acos;
8mod acosh;
9mod asin;
10mod asinh;
11mod atan;
12mod atan2;
13mod atanh;
14mod cos;
15mod exp;
16mod floor;
17mod k_cos;
18mod k_sin;
19pub(crate) mod k_tan;
20mod ln;
21pub(crate) mod log1p;
22mod pow;
23mod rem_pio2;
24mod rem_pio2_large;
25pub(crate) mod scalbn;
26mod sin;
27mod tan;
28pub use acos::acos;
29pub use acosh::acosh;
30pub use asin::asin;
31pub use asinh::asinh;
32pub use atan::atan;
33pub use atan2::atan2;
34pub use atanh::atanh;
35pub use cos::cos;
36pub use exp::exp;
37pub use floor::floor;
38pub use ln::ln;
39pub use pow::pow;
40pub use sin::sin;
41pub use tan::tan;
42
43/// Number of sum iterations for Taylor series
44const TAYLOR_SERIES_SUMS: usize = 16;
45
46/// Cotangent
47///
48/// ```
49/// # use trig_const::cot;
50/// # use core::f64::consts::PI;
51/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
52/// const COT_PI_4: f64 = cot(PI / 4.0);
53/// float_eq(COT_PI_4, 1.0);
54/// ```
55pub const fn cot(x: f64) -> f64 {
56    let sin_calc = sin(x);
57    if sin_calc == 0.0 {
58        f64::INFINITY
59    } else {
60        cos(x) / sin_calc
61    }
62}
63
64/// Secant
65///
66/// ```
67/// # use trig_const::sec;
68/// # use core::f64::consts::PI;
69/// const SEC_PI: f64 = sec(PI);
70/// assert_eq!(SEC_PI, -1.0);
71/// ```
72pub const fn sec(x: f64) -> f64 {
73    let cos_calc = cos(x);
74    if cos_calc == 0.0 {
75        f64::INFINITY
76    } else {
77        1.0 / cos_calc
78    }
79}
80
81/// Cosecant
82///
83/// ```
84/// # use trig_const::csc;
85/// # use core::f64::consts::PI;
86/// const CSC_PI_2: f64 = csc(PI / 2.0);
87/// assert_eq!(CSC_PI_2, 1.0);
88/// ```
89pub const fn csc(x: f64) -> f64 {
90    let sin_calc = sin(x);
91    if sin_calc == 0.0 {
92        f64::INFINITY
93    } else {
94        1.0 / sin_calc
95    }
96}
97
98/// Hyperbolic Sine
99///
100/// ```
101/// # use trig_const::sinh;
102/// const SINH_0: f64 = sinh(0.0);
103/// assert_eq!(SINH_0, 0.0);
104/// ```
105pub const fn sinh(x: f64) -> f64 {
106    (exp(x) - exp(-x)) / 2.0
107}
108
109/// Hyperbolic Cosine
110///
111/// ```
112/// # use trig_const::cosh;
113/// const COSH_0: f64 = cosh(0.0);
114/// assert_eq!(COSH_0, 1.0);
115/// ```
116pub const fn cosh(x: f64) -> f64 {
117    (exp(x) + exp(-x)) / 2.0
118}
119
120/// x^pow
121pub const fn expi(x: f64, mut pow: isize) -> f64 {
122    let mut o = 1.0;
123
124    while pow > 0 {
125        o *= x;
126        pow -= 1;
127    }
128    while pow < 0 {
129        o /= x;
130        pow += 1;
131    }
132
133    o
134}
135
136/// Factorial (x!)
137pub const fn factorial(mut x: f64) -> f64 {
138    if x == 0.0 || x == 1.0 {
139        1.0
140    } else {
141        let mut s = 1.0;
142        while x > 1.0 {
143            s *= x;
144            x -= 1.0;
145        }
146        s
147    }
148}
149
150/// Const sqrt function using Newton's method
151pub const fn sqrt(x: f64) -> f64 {
152    if x.is_nan() || x < 0.0 {
153        return f64::NAN;
154    } else if x.is_infinite() || x == 0.0 {
155        return x;
156    }
157
158    // Use Newton's method for sqrt calculation
159    let mut current_guess = 1.0;
160
161    let mut i = 0;
162    while i < TAYLOR_SERIES_SUMS {
163        current_guess = 0.5 * (current_guess + x / current_guess);
164        i += 1;
165    }
166
167    current_guess
168}
169
170pub const fn fabs(x: f64) -> f64 {
171    if x > 0.0 {
172        x
173    } else {
174        -x
175    }
176}
177
178const fn with_set_high_word(f: f64, hi: u32) -> f64 {
179    let mut tmp = f.to_bits();
180    tmp &= 0x00000000_ffffffff;
181    tmp |= (hi as u64) << 32;
182    f64::from_bits(tmp)
183}
184const fn with_set_low_word(f: f64, lo: u32) -> f64 {
185    let mut tmp = f.to_bits();
186    tmp &= 0xffffffff_00000000;
187    tmp |= lo as u64;
188    f64::from_bits(tmp)
189}
190const fn get_high_word(x: f64) -> u32 {
191    (x.to_bits() >> 32) as u32
192}
193
194const fn get_low_word(x: f64) -> u32 {
195    x.to_bits() as u32
196}
197
198#[cfg(test)]
199mod tests {
200    use core::f64::consts::{E, PI};
201
202    use crate::{cos, cosh, exp, expi, factorial, ln, sin, sinh, sqrt};
203
204    macro_rules! float_eq {
205        ($lhs:expr, $rhs:expr) => {
206            assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
207        };
208    }
209
210    #[test]
211    fn test_factorial() {
212        assert_eq!(factorial(0.0), 1.0);
213        assert_eq!(factorial(1.0), 1.0);
214        assert_eq!(factorial(2.0), 2.0);
215        assert_eq!(factorial(3.0), 6.0);
216        assert_eq!(factorial(4.0), 24.0);
217        assert_eq!(factorial(5.0), 120.0);
218    }
219
220    #[test]
221    fn test_expi() {
222        assert_eq!(expi(2.0, 0), 1.0);
223        assert_eq!(expi(2.0, 4), 16.0);
224        assert_eq!(expi(2.0, 5), 32.0);
225        assert_eq!(expi(3.0, 3), 27.0);
226    }
227
228    #[test]
229    fn test_exp() {
230        float_eq!(exp(0.0), 1.0);
231        float_eq!(exp(1.0), E);
232    }
233
234    #[test]
235    fn test_sqrt() {
236        float_eq!(sqrt(0.0), 0.0);
237        float_eq!(sqrt(1.0), 1.0);
238        float_eq!(sqrt(4.0), 2.0);
239        float_eq!(sqrt(9.0), 3.0);
240        float_eq!(sqrt(16.0), 4.0);
241        float_eq!(sqrt(25.0), 5.0);
242    }
243
244    #[test]
245    fn test_cos() {
246        float_eq!(cos(0.0), 0.0_f64.cos());
247        float_eq!(cos(1.0), 1.0_f64.cos());
248        float_eq!(cos(PI), PI.cos());
249        float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
250    }
251
252    #[test]
253    fn test_sin() {
254        float_eq!(sin(0.0), 0.0_f64.sin());
255        float_eq!(sin(1.0), 1.0_f64.sin());
256        float_eq!(sin(PI), PI.sin());
257        float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
258    }
259
260    #[test]
261    fn test_sinh() {
262        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
263            float_eq!(sinh(x), x.sinh());
264        }
265    }
266
267    #[test]
268    fn test_cosh() {
269        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
270            float_eq!(cosh(x), x.cosh());
271        }
272    }
273
274    #[test]
275    fn test_ln() {
276        // float_eq!(ln(0.01), 0.01_f64.ln());
277        // float_eq!(ln(0.5), 0.5_f64.ln());
278        float_eq!(ln(1.0), 1.0_f64.ln());
279        float_eq!(ln(2.0), 2.0_f64.ln());
280        float_eq!(ln(10.0), 10.0_f64.ln());
281        float_eq!(ln(1_000.0), 1_000.0_f64.ln());
282    }
283}