Skip to main content

bop/
math.rs

1//! Tiny `f64` math facade that works on both the default (std)
2//! build and the `no_std` opt-in. `core::f64` doesn't expose
3//! `sqrt` / `sin` / `cos` / etc. — those methods live in
4//! `std::f64`'s platform-dependent math library — so the no_std
5//! build has to forward to the pure-Rust `libm` crate instead.
6//! The std build stays dep-free and calls the native `f64`
7//! methods directly.
8//!
9//! Every bop math builtin and `ops::div` / `ops::rem`'s float
10//! path goes through here rather than calling `x.sqrt()` /
11//! `libm::sqrt(x)` at the point of use, so the rest of the
12//! codebase stays `#[cfg]`-free.
13//!
14//! ## Feature
15//!
16//! The only math-related feature is `no_std`. Leave it off (the
17//! default) to get `std::f64`'s native math with no external
18//! deps. Enable it — with `default-features = false, features =
19//! ["no_std"]` — when targeting bare-metal / embedded / edge
20//! wasm. `no_std` pulls in the tiny pure-Rust `libm` crate.
21
22#![allow(dead_code)]
23
24/// `√x`.
25#[inline]
26pub fn sqrt(x: f64) -> f64 {
27    #[cfg(not(feature = "no_std"))]
28    {
29        x.sqrt()
30    }
31    #[cfg(feature = "no_std")]
32    {
33        libm::sqrt(x)
34    }
35}
36
37/// `sin(x)` (radians).
38#[inline]
39pub fn sin(x: f64) -> f64 {
40    #[cfg(not(feature = "no_std"))]
41    {
42        x.sin()
43    }
44    #[cfg(feature = "no_std")]
45    {
46        libm::sin(x)
47    }
48}
49
50/// `cos(x)` (radians).
51#[inline]
52pub fn cos(x: f64) -> f64 {
53    #[cfg(not(feature = "no_std"))]
54    {
55        x.cos()
56    }
57    #[cfg(feature = "no_std")]
58    {
59        libm::cos(x)
60    }
61}
62
63/// `tan(x)` (radians).
64#[inline]
65pub fn tan(x: f64) -> f64 {
66    #[cfg(not(feature = "no_std"))]
67    {
68        x.tan()
69    }
70    #[cfg(feature = "no_std")]
71    {
72        libm::tan(x)
73    }
74}
75
76/// `⌊x⌋` — largest integer ≤ x, as a float.
77#[inline]
78pub fn floor(x: f64) -> f64 {
79    #[cfg(not(feature = "no_std"))]
80    {
81        x.floor()
82    }
83    #[cfg(feature = "no_std")]
84    {
85        libm::floor(x)
86    }
87}
88
89/// `⌈x⌉` — smallest integer ≥ x, as a float.
90#[inline]
91pub fn ceil(x: f64) -> f64 {
92    #[cfg(not(feature = "no_std"))]
93    {
94        x.ceil()
95    }
96    #[cfg(feature = "no_std")]
97    {
98        libm::ceil(x)
99    }
100}
101
102/// Round half-away-from-zero (matches `f64::round`).
103#[inline]
104pub fn round(x: f64) -> f64 {
105    #[cfg(not(feature = "no_std"))]
106    {
107        x.round()
108    }
109    #[cfg(feature = "no_std")]
110    {
111        libm::round(x)
112    }
113}
114
115/// Truncate toward zero — drop the fractional part.
116#[inline]
117pub fn trunc(x: f64) -> f64 {
118    #[cfg(not(feature = "no_std"))]
119    {
120        x.trunc()
121    }
122    #[cfg(feature = "no_std")]
123    {
124        libm::trunc(x)
125    }
126}
127
128/// `base ** exp` for floats.
129#[inline]
130pub fn powf(base: f64, exp: f64) -> f64 {
131    #[cfg(not(feature = "no_std"))]
132    {
133        base.powf(exp)
134    }
135    #[cfg(feature = "no_std")]
136    {
137        libm::pow(base, exp)
138    }
139}
140
141/// Natural log.
142#[inline]
143pub fn ln(x: f64) -> f64 {
144    #[cfg(not(feature = "no_std"))]
145    {
146        x.ln()
147    }
148    #[cfg(feature = "no_std")]
149    {
150        libm::log(x)
151    }
152}
153
154/// `e^x`.
155#[inline]
156pub fn exp(x: f64) -> f64 {
157    #[cfg(not(feature = "no_std"))]
158    {
159        x.exp()
160    }
161    #[cfg(feature = "no_std")]
162    {
163        libm::exp(x)
164    }
165}