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 = "1.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    let div = (x / PI) as u32;
48    x = x - (div as f64 * PI);
49    let sign = if div % 2 != 0 { -1.0 } else { 1.0 };
50
51    let mut result = 1.0;
52    let mut inter = 1.0;
53    let num = x * x;
54
55    let mut i = 1;
56    while i <= 16 {
57        let comp = 2.0 * i as f64;
58        let den = comp * (comp - 1.0);
59        inter *= num / den;
60        if i % 2 == 0 {
61            result += inter;
62        } else {
63            result -= inter;
64        }
65        i += 1;
66    }
67
68    sign * result
69}
70
71/// Sine
72///
73/// ```
74/// # use trig_const::sin;
75/// # use core::f64::consts::PI;
76/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
77/// const SIN_PI: f64 = sin(PI);
78/// float_eq(SIN_PI, 0.0);
79/// ```
80pub const fn sin(x: f64) -> f64 {
81    cos(x - PI / 2.0)
82}
83
84/// Tangent
85///
86/// ```
87/// # use trig_const::tan;
88/// # use core::f64::consts::PI;
89/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
90/// const TAN_PI_4: f64 = tan(PI / 4.0);
91/// float_eq(TAN_PI_4, 1.0);
92/// ```
93pub const fn tan(x: f64) -> f64 {
94    sin(x) / cos(x)
95}
96
97/// Cotangent
98///
99/// ```
100/// # use trig_const::cot;
101/// # use core::f64::consts::PI;
102/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
103/// const COT_PI_4: f64 = cot(PI / 4.0);
104/// float_eq(COT_PI_4, 1.0);
105/// ```
106pub const fn cot(x: f64) -> f64 {
107    cos(x) / sin(x)
108}
109
110/// Secant
111///
112/// ```
113/// # use trig_const::sec;
114/// # use core::f64::consts::PI;
115/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
116/// let SEC_PI: f64 = sec(PI);
117/// float_eq(SEC_PI, -1.0);
118/// ```
119pub const fn sec(x: f64) -> f64 {
120    1.0 / cos(x)
121}
122
123/// Cosecant
124///
125/// ```
126/// # use trig_const::csc;
127/// # use core::f64::consts::PI;
128/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
129/// const CSC_PI_2: f64 = csc(PI / 2.0);
130/// float_eq(CSC_PI_2, 1.0);
131/// ```
132pub const fn csc(x: f64) -> f64 {
133    1.0 / sin(x)
134}
135
136#[cfg(test)]
137mod tests {
138    use core::f64::consts::PI;
139
140    use crate::{cos, sin};
141
142    macro_rules! float_eq {
143        ($lhs:expr, $rhs:expr) => {
144            assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
145        };
146    }
147
148    #[test]
149    fn test_cos() {
150        float_eq!(cos(0.0), 0.0_f64.cos());
151        float_eq!(cos(1.0), 1.0_f64.cos());
152        float_eq!(cos(PI), PI.cos());
153        float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
154    }
155
156    #[test]
157    fn test_sin() {
158        float_eq!(sin(0.0), 0.0_f64.sin());
159        float_eq!(sin(1.0), 1.0_f64.sin());
160        float_eq!(sin(PI), PI.sin());
161        float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
162    }
163}