pub fn sqrt(x: f32) -> f32 {
libm::sqrtf(x)
}
pub fn hypot(x: f32, y: f32) -> f32 {
libm::hypotf(x, y)
}
pub fn sin(x: f32) -> f32 {
libm::sinf(x)
}
pub fn cos(x: f32) -> f32 {
libm::cosf(x)
}
pub fn sin_cos(x: f32) -> (f32, f32) {
libm::sincosf(x)
}
pub fn tan(x: f32) -> f32 {
libm::tanf(x)
}
pub fn asin(x: f32) -> f32 {
libm::asinf(x)
}
pub fn acos(x: f32) -> f32 {
libm::acosf(x)
}
pub fn atan2(y: f32, x: f32) -> f32 {
libm::atan2f(y, x)
}
pub fn floor(x: f32) -> f32 {
libm::floorf(x)
}
pub fn ceil(x: f32) -> f32 {
libm::ceilf(x)
}
pub fn round(x: f32) -> f32 {
libm::roundf(x)
}
pub fn trunc(x: f32) -> f32 {
libm::truncf(x)
}
pub fn fract(x: f32) -> f32 {
x - trunc(x)
}
pub fn exp(x: f32) -> f32 {
libm::expf(x)
}
pub fn exp2(x: f32) -> f32 {
libm::exp2f(x)
}
pub fn ln(x: f32) -> f32 {
libm::logf(x)
}
pub fn log2(x: f32) -> f32 {
libm::log2f(x)
}
pub fn powf(x: f32, n: f32) -> f32 {
libm::powf(x, n)
}
pub fn powi(x: f32, n: i32) -> f32 {
let mut base = x;
let mut exp = n;
let mut acc = 1.0;
loop {
if exp & 1 != 0 {
acc *= base;
}
exp /= 2;
if exp == 0 {
break;
}
base *= base;
}
if n < 0 { 1.0 / acc } else { acc }
}
pub fn mul_add(x: f32, y: f32, z: f32) -> f32 {
libm::fmaf(x, y, z)
}
pub fn rem_euclid(x: f32, rhs: f32) -> f32 {
let r = libm::fmodf(x, rhs);
if r < 0.0 { r + libm::fabsf(rhs) } else { r }
}
#[cfg(test)]
mod tests {
use super::*;
#[track_caller]
fn approx(got: f32, want: f32) {
assert!((got - want).abs() < 1e-6, "got {got}, want {want}");
}
#[track_caller]
fn approx_rel(got: f32, want: f32) {
let scale = want.abs().max(1.0);
assert!((got - want).abs() <= 1e-6 * scale, "got {got}, want {want}");
}
#[test]
fn transcendentals_match_std() {
for &x in &[0.0f32, 0.5, 1.0, 2.5, 7.0] {
approx(sqrt(x), f32::sqrt(x));
approx(exp(x), f32::exp(x));
approx(exp2(x), f32::exp2(x));
approx(powf(x, 1.5), f32::powf(x, 1.5));
approx_rel(hypot(x, 3.0), f32::hypot(x, 3.0));
}
for &x in &[0.25f32, 0.5, 1.0, 2.5, 7.0, 1000.0] {
approx(ln(x), f32::ln(x));
approx(log2(x), f32::log2(x));
}
for &x in &[-2.5f32, -0.75, 0.0, 0.3, 1.2, 3.0] {
approx(sin(x), f32::sin(x));
approx(cos(x), f32::cos(x));
approx(tan(x), f32::tan(x));
approx(floor(x), f32::floor(x));
approx(ceil(x), f32::ceil(x));
approx(round(x), f32::round(x));
approx(trunc(x), f32::trunc(x));
approx(fract(x), f32::fract(x));
approx(atan2(x, 2.0), f32::atan2(x, 2.0));
approx(mul_add(x, 2.5, -1.25), f32::mul_add(x, 2.5, -1.25));
let (s, c) = sin_cos(x);
approx(s, f32::sin(x));
approx(c, f32::cos(x));
}
for &x in &[-1.0f32, -0.5, 0.0, 0.5, 1.0] {
approx(asin(x), f32::asin(x));
approx(acos(x), f32::acos(x));
}
}
#[test]
fn rounding_matches_std_at_the_halves_and_across_signs() {
for &x in &[-2.5f32, -1.5, -0.5, -0.25, 0.0, 0.25, 0.5, 1.5, 2.5] {
approx(floor(x), f32::floor(x));
approx(ceil(x), f32::ceil(x));
approx(round(x), f32::round(x));
approx(trunc(x), f32::trunc(x));
approx(fract(x), f32::fract(x));
}
}
#[test]
fn powi_matches_std_across_exponent_signs() {
for &x in &[-3.0f32, -0.5, 0.5, 1.0, 2.0, 7.5] {
for n in -6i32..=6 {
approx_rel(powi(x, n), f32::powi(x, n));
}
}
assert_eq!(powi(0.0, 0), 1.0);
assert_eq!(powi(5.0, 1), 5.0);
}
#[test]
fn mul_add_rounds_once() {
let x = 8_388_609.0f32;
let z = -(x * x);
assert_eq!(mul_add(x, x, z), f32::mul_add(x, x, z));
assert_eq!(mul_add(x, x, z), 1.0);
assert_eq!(x * x + z, 0.0);
}
#[test]
fn rem_euclid_matches_std_across_signs() {
for &(a, b) in &[
(7.5f32, 2.0f32),
(-7.5, 2.0),
(7.5, -2.0),
(-7.5, -2.0),
(0.0, 3.0),
(-0.25, 1.0),
] {
approx(rem_euclid(a, b), f32::rem_euclid(a, b));
assert!(rem_euclid(a, b) >= 0.0, "{a} rem_euclid {b}");
}
}
}