trig_const/
lib.rs

1//! ## trig-const
2//!
3//! Rust implementation of const trig functions.
4//!
5//! This is implemented using a 16-term Taylor series approximation of cosine.
6//! Correctness is favored over speed, especially considering the main use case for
7//! this crate is to expose trigonometric functions for compile time.
8//!
9//! The implemntation was largely inspired by the work of Dr. Austin Henley and Dr. Stephen Marz:
10//!   - GitHub Repo: <https://github.com/AZHenley/cosine>
11//!   - Article: <https://austinhenley.com/blog/cosine.html>
12//!
13//! The implementation carries forward the original MIT license contained in the GitHub repo above.
14//!
15//! ## Requirements
16//!
17//! This crate supports any compiler version back to rustc 1.85
18//!
19//! ```toml
20//! [dependencies]
21//! trig-const = "0"
22//! ```
23//!
24//! ## Example
25//!
26//! ```
27//! # use trig_const::cos;
28//! # use core::f64::consts::PI;
29//! # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
30//! const COS_PI: f64 = cos(PI);
31//! float_eq(COS_PI, -1.0);
32//! ```
33
34#![no_std]
35use core::f64::consts::PI;
36
37/// Cosine
38///
39/// ```
40/// # use trig_const::cos;
41/// # use core::f64::consts::PI;
42/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
43/// const COS_PI: f64 = cos(PI);
44/// float_eq(COS_PI, -1.0);
45/// ```
46pub const fn cos(mut x: f64) -> f64 {
47    // If value is large, fold into smaller value
48    while x < -0.1 {
49        x += 2.0 * PI;
50    }
51    while x > 2.0 * PI + 0.1 {
52        x -= 2.0 * PI;
53    }
54    let div = (x / PI) as u32;
55    x = x - (div as f64 * PI);
56    let sign = if div % 2 != 0 { -1.0 } else { 1.0 };
57
58    let mut result = 1.0;
59    let mut inter = 1.0;
60    let num = x * x;
61
62    let mut i = 1;
63    while i <= 16 {
64        let comp = 2.0 * i as f64;
65        let den = comp * (comp - 1.0);
66        inter *= num / den;
67        if i % 2 == 0 {
68            result += inter;
69        } else {
70            result -= inter;
71        }
72        i += 1;
73    }
74
75    sign * result
76}
77
78/// Sine
79///
80/// ```
81/// # use trig_const::sin;
82/// # use core::f64::consts::PI;
83/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
84/// const SIN_PI: f64 = sin(PI);
85/// float_eq(SIN_PI, 0.0);
86/// ```
87pub const fn sin(x: f64) -> f64 {
88    cos(x - PI / 2.0)
89}
90
91/// Tangent
92///
93/// ```
94/// # use trig_const::tan;
95/// # use core::f64::consts::PI;
96/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
97/// const TAN_PI_4: f64 = tan(PI / 4.0);
98/// float_eq(TAN_PI_4, 1.0);
99/// ```
100pub const fn tan(x: f64) -> f64 {
101    sin(x) / cos(x)
102}
103
104/// Cotangent
105///
106/// ```
107/// # use trig_const::cot;
108/// # use core::f64::consts::PI;
109/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
110/// const COT_PI_4: f64 = cot(PI / 4.0);
111/// float_eq(COT_PI_4, 1.0);
112/// ```
113pub const fn cot(x: f64) -> f64 {
114    cos(x) / sin(x)
115}
116
117/// Secant
118///
119/// ```
120/// # use trig_const::sec;
121/// # use core::f64::consts::PI;
122/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
123/// let SEC_PI: f64 = sec(PI);
124/// float_eq(SEC_PI, -1.0);
125/// ```
126pub const fn sec(x: f64) -> f64 {
127    1.0 / cos(x)
128}
129
130/// Cosecant
131///
132/// ```
133/// # use trig_const::csc;
134/// # use core::f64::consts::PI;
135/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
136/// const CSC_PI_2: f64 = csc(PI / 2.0);
137/// float_eq(CSC_PI_2, 1.0);
138/// ```
139pub const fn csc(x: f64) -> f64 {
140    1.0 / sin(x)
141}
142
143#[cfg(test)]
144mod tests {
145    use core::f64::consts::PI;
146
147    use crate::{cos, sin};
148
149    macro_rules! float_eq {
150        ($lhs:expr, $rhs:expr) => {
151            assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
152        };
153    }
154
155    #[test]
156    fn test_cos() {
157        float_eq!(cos(0.0), 0.0_f64.cos());
158        float_eq!(cos(1.0), 1.0_f64.cos());
159        float_eq!(cos(PI), PI.cos());
160        float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
161    }
162
163    #[test]
164    fn test_sin() {
165        float_eq!(sin(0.0), 0.0_f64.sin());
166        float_eq!(sin(1.0), 1.0_f64.sin());
167        float_eq!(sin(PI), PI.sin());
168        float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
169    }
170}