Skip to main content

trig_const/
lib.rs

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