1pub fn sqrt(x: f32) -> f32 {
9 libm::sqrtf(x)
10}
11
12pub fn hypot(x: f32, y: f32) -> f32 {
15 libm::hypotf(x, y)
16}
17
18pub fn sin(x: f32) -> f32 {
20 libm::sinf(x)
21}
22
23pub fn cos(x: f32) -> f32 {
25 libm::cosf(x)
26}
27
28pub fn sin_cos(x: f32) -> (f32, f32) {
30 libm::sincosf(x)
31}
32
33pub fn tan(x: f32) -> f32 {
35 libm::tanf(x)
36}
37
38pub fn asin(x: f32) -> f32 {
40 libm::asinf(x)
41}
42
43pub fn acos(x: f32) -> f32 {
45 libm::acosf(x)
46}
47
48pub fn atan2(y: f32, x: f32) -> f32 {
50 libm::atan2f(y, x)
51}
52
53pub fn floor(x: f32) -> f32 {
55 libm::floorf(x)
56}
57
58pub fn ceil(x: f32) -> f32 {
60 libm::ceilf(x)
61}
62
63pub fn round(x: f32) -> f32 {
65 libm::roundf(x)
66}
67
68pub fn trunc(x: f32) -> f32 {
70 libm::truncf(x)
71}
72
73pub fn fract(x: f32) -> f32 {
75 x - trunc(x)
76}
77
78pub fn exp(x: f32) -> f32 {
80 libm::expf(x)
81}
82
83pub fn exp2(x: f32) -> f32 {
85 libm::exp2f(x)
86}
87
88pub fn ln(x: f32) -> f32 {
90 libm::logf(x)
91}
92
93pub fn log2(x: f32) -> f32 {
95 libm::log2f(x)
96}
97
98pub fn powf(x: f32, n: f32) -> f32 {
100 libm::powf(x, n)
101}
102
103pub fn powi(x: f32, n: i32) -> f32 {
107 let mut base = x;
108 let mut exp = n;
109 let mut acc = 1.0;
110 loop {
111 if exp & 1 != 0 {
112 acc *= base;
113 }
114 exp /= 2;
117 if exp == 0 {
118 break;
119 }
120 base *= base;
121 }
122 if n < 0 { 1.0 / acc } else { acc }
123}
124
125pub fn mul_add(x: f32, y: f32, z: f32) -> f32 {
127 libm::fmaf(x, y, z)
128}
129
130pub fn rem_euclid(x: f32, rhs: f32) -> f32 {
132 let r = libm::fmodf(x, rhs);
133 if r < 0.0 { r + libm::fabsf(rhs) } else { r }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[track_caller]
145 fn approx(got: f32, want: f32) {
146 assert!((got - want).abs() < 1e-6, "got {got}, want {want}");
147 }
148
149 #[track_caller]
152 fn approx_rel(got: f32, want: f32) {
153 let scale = want.abs().max(1.0);
154 assert!((got - want).abs() <= 1e-6 * scale, "got {got}, want {want}");
155 }
156
157 #[test]
158 fn transcendentals_match_std() {
159 for &x in &[0.0f32, 0.5, 1.0, 2.5, 7.0] {
160 approx(sqrt(x), f32::sqrt(x));
161 approx(exp(x), f32::exp(x));
162 approx(exp2(x), f32::exp2(x));
163 approx(powf(x, 1.5), f32::powf(x, 1.5));
164 approx_rel(hypot(x, 3.0), f32::hypot(x, 3.0));
165 }
166 for &x in &[0.25f32, 0.5, 1.0, 2.5, 7.0, 1000.0] {
167 approx(ln(x), f32::ln(x));
168 approx(log2(x), f32::log2(x));
169 }
170 for &x in &[-2.5f32, -0.75, 0.0, 0.3, 1.2, 3.0] {
171 approx(sin(x), f32::sin(x));
172 approx(cos(x), f32::cos(x));
173 approx(tan(x), f32::tan(x));
174 approx(floor(x), f32::floor(x));
175 approx(ceil(x), f32::ceil(x));
176 approx(round(x), f32::round(x));
177 approx(trunc(x), f32::trunc(x));
178 approx(fract(x), f32::fract(x));
179 approx(atan2(x, 2.0), f32::atan2(x, 2.0));
180 approx(mul_add(x, 2.5, -1.25), f32::mul_add(x, 2.5, -1.25));
181 let (s, c) = sin_cos(x);
182 approx(s, f32::sin(x));
183 approx(c, f32::cos(x));
184 }
185 for &x in &[-1.0f32, -0.5, 0.0, 0.5, 1.0] {
186 approx(asin(x), f32::asin(x));
187 approx(acos(x), f32::acos(x));
188 }
189 }
190
191 #[test]
194 fn rounding_matches_std_at_the_halves_and_across_signs() {
195 for &x in &[-2.5f32, -1.5, -0.5, -0.25, 0.0, 0.25, 0.5, 1.5, 2.5] {
196 approx(floor(x), f32::floor(x));
197 approx(ceil(x), f32::ceil(x));
198 approx(round(x), f32::round(x));
199 approx(trunc(x), f32::trunc(x));
200 approx(fract(x), f32::fract(x));
201 }
202 }
203
204 #[test]
208 fn powi_matches_std_across_exponent_signs() {
209 for &x in &[-3.0f32, -0.5, 0.5, 1.0, 2.0, 7.5] {
210 for n in -6i32..=6 {
211 approx_rel(powi(x, n), f32::powi(x, n));
212 }
213 }
214 assert_eq!(powi(0.0, 0), 1.0);
215 assert_eq!(powi(5.0, 1), 5.0);
216 }
217
218 #[test]
222 fn mul_add_rounds_once() {
223 let x = 8_388_609.0f32;
224 let z = -(x * x);
225 assert_eq!(mul_add(x, x, z), f32::mul_add(x, x, z));
226 assert_eq!(mul_add(x, x, z), 1.0);
227 assert_eq!(x * x + z, 0.0);
228 }
229
230 #[test]
233 fn rem_euclid_matches_std_across_signs() {
234 for &(a, b) in &[
235 (7.5f32, 2.0f32),
236 (-7.5, 2.0),
237 (7.5, -2.0),
238 (-7.5, -2.0),
239 (0.0, 3.0),
240 (-0.25, 1.0),
241 ] {
242 approx(rem_euclid(a, b), f32::rem_euclid(a, b));
243 assert!(rem_euclid(a, b) >= 0.0, "{a} rem_euclid {b}");
244 }
245 }
246}