From c8aabbdf8e763eb9931e7c4cf3bd3ed4fc17c63f Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 13 Jan 2026 10:37:43 +0900 Subject: [PATCH] m_rust for wasm --- Cargo.toml | 4 + src/err.rs | 10 ++- src/lib.rs | 7 ++ src/m_rust.rs | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/m_sys.rs | 38 +-------- 5 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 src/m_rust.rs diff --git a/Cargo.toml b/Cargo.toml index c6751ee..573397e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,10 @@ _bigint = ["dep:num-traits", "dep:num-integer"] # Internal feature. User must u # See also: https://github.com/python/cpython/issues/132763 mul_add = [] +# For WASM and other targets without native libm +[target.'cfg(not(any(unix, windows)))'.dependencies] +libm = "0.2" + [dependencies] libc = "0.2" num-complex = { version = "0.4", optional = true } diff --git a/src/err.rs b/src/err.rs index 65be9bb..639f96c 100644 --- a/src/err.rs +++ b/src/err.rs @@ -44,7 +44,10 @@ pub(crate) fn set_errno(value: i32) { unsafe { *_errno() = value; } - #[cfg(all(unix, not(any(target_os = "linux", target_os = "android", target_os = "macos"))))] + #[cfg(all( + unix, + not(any(target_os = "linux", target_os = "android", target_os = "macos")) + ))] unsafe { // FreeBSD, NetBSD, OpenBSD, etc. use __error() *libc::__error() = value; @@ -73,7 +76,10 @@ pub(crate) fn get_errno() -> i32 { unsafe { *_errno() } - #[cfg(all(unix, not(any(target_os = "linux", target_os = "android", target_os = "macos"))))] + #[cfg(all( + unix, + not(any(target_os = "linux", target_os = "android", target_os = "macos")) + ))] unsafe { // FreeBSD, NetBSD, OpenBSD, etc. use __error() *libc::__error() diff --git a/src/lib.rs b/src/lib.rs index c3a0db4..a3d1267 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,14 @@ pub mod math; // Internal modules mod err; +// Native libm via FFI (unix/windows) +#[cfg(any(unix, windows))] pub(crate) mod m; +// Pure Rust libm for WASM and other targets +#[cfg(not(any(unix, windows)))] +#[path = "m_rust.rs"] +pub(crate) mod m; +#[cfg(any(unix, windows))] mod m_sys; #[cfg(test)] mod test; diff --git a/src/m_rust.rs b/src/m_rust.rs new file mode 100644 index 0000000..fa9ba14 --- /dev/null +++ b/src/m_rust.rs @@ -0,0 +1,211 @@ +//! Pure Rust implementations of libm functions using the libm crate. +//! Used on targets without native libm (e.g., WASM). + +// Trigonometric functions + +#[inline(always)] +pub fn acos(n: f64) -> f64 { + libm::acos(n) +} + +#[inline(always)] +pub fn asin(n: f64) -> f64 { + libm::asin(n) +} + +#[inline(always)] +pub fn atan(n: f64) -> f64 { + libm::atan(n) +} + +#[inline(always)] +pub fn atan2(y: f64, x: f64) -> f64 { + libm::atan2(y, x) +} + +#[inline(always)] +pub fn cos(n: f64) -> f64 { + libm::cos(n) +} + +#[inline(always)] +pub fn sin(n: f64) -> f64 { + libm::sin(n) +} + +#[inline(always)] +pub fn tan(n: f64) -> f64 { + libm::tan(n) +} + +// Hyperbolic functions + +#[inline(always)] +pub fn acosh(n: f64) -> f64 { + libm::acosh(n) +} + +#[inline(always)] +pub fn asinh(n: f64) -> f64 { + libm::asinh(n) +} + +#[inline(always)] +pub fn atanh(n: f64) -> f64 { + libm::atanh(n) +} + +#[inline(always)] +pub fn cosh(n: f64) -> f64 { + libm::cosh(n) +} + +#[inline(always)] +pub fn sinh(n: f64) -> f64 { + libm::sinh(n) +} + +#[inline(always)] +pub fn tanh(n: f64) -> f64 { + libm::tanh(n) +} + +// Exponential and logarithmic functions + +#[inline(always)] +pub fn exp(n: f64) -> f64 { + libm::exp(n) +} + +#[inline(always)] +pub fn exp2(n: f64) -> f64 { + libm::exp2(n) +} + +#[inline(always)] +pub fn expm1(n: f64) -> f64 { + libm::expm1(n) +} + +#[inline(always)] +pub fn log(n: f64) -> f64 { + libm::log(n) +} + +#[inline(always)] +pub fn log10(n: f64) -> f64 { + libm::log10(n) +} + +#[inline(always)] +pub fn log1p(n: f64) -> f64 { + libm::log1p(n) +} + +#[inline(always)] +pub fn log2(n: f64) -> f64 { + libm::log2(n) +} + +// Power functions + +#[inline(always)] +pub fn cbrt(n: f64) -> f64 { + libm::cbrt(n) +} + +#[inline(always)] +pub fn hypot(x: f64, y: f64) -> f64 { + libm::hypot(x, y) +} + +#[inline(always)] +pub fn pow(x: f64, y: f64) -> f64 { + libm::pow(x, y) +} + +#[inline(always)] +pub fn sqrt(n: f64) -> f64 { + libm::sqrt(n) +} + +// Floating-point manipulation functions + +#[inline(always)] +pub fn ceil(n: f64) -> f64 { + libm::ceil(n) +} + +#[inline(always)] +pub fn copysign(x: f64, y: f64) -> f64 { + libm::copysign(x, y) +} + +#[inline(always)] +pub fn fabs(n: f64) -> f64 { + libm::fabs(n) +} + +#[inline(always)] +pub fn floor(n: f64) -> f64 { + libm::floor(n) +} + +#[inline(always)] +pub fn fmod(x: f64, y: f64) -> f64 { + libm::fmod(x, y) +} + +#[inline(always)] +pub fn frexp(n: f64, exp: &mut i32) -> f64 { + let (mantissa, exponent) = libm::frexp(n); + *exp = exponent; + mantissa +} + +#[inline(always)] +pub fn ldexp(x: f64, n: i32) -> f64 { + libm::ldexp(x, n) +} + +#[inline(always)] +pub fn modf(n: f64, iptr: &mut f64) -> f64 { + let (frac, int) = libm::modf(n); + *iptr = int; + frac +} + +#[inline(always)] +pub fn nextafter(x: f64, y: f64) -> f64 { + libm::nextafter(x, y) +} + +#[inline(always)] +pub fn remainder(x: f64, y: f64) -> f64 { + libm::remainder(x, y) +} + +#[inline(always)] +pub fn trunc(n: f64) -> f64 { + libm::trunc(n) +} + +// Special functions + +#[inline(always)] +pub fn erf(n: f64) -> f64 { + libm::erf(n) +} + +#[inline(always)] +pub fn erfc(n: f64) -> f64 { + libm::erfc(n) +} + +// Platform-specific sincos (fallback: call sin and cos separately) + +#[cfg(feature = "complex")] +#[inline(always)] +pub fn sincos(x: f64) -> (f64, f64) { + (libm::sin(x), libm::cos(x)) +} diff --git a/src/m_sys.rs b/src/m_sys.rs index 60406a8..5d10d5d 100644 --- a/src/m_sys.rs +++ b/src/m_sys.rs @@ -1,5 +1,7 @@ -// These symbols are all defined by `libm`, -// or by `compiler-builtins` on unsupported platforms. +// Raw FFI declarations for system libm. +// Only available on unix/windows where native libm exists. + +#[cfg(any(unix, windows))] #[cfg_attr(unix, link(name = "m"))] #[allow(dead_code)] unsafe extern "C" { @@ -45,7 +47,6 @@ unsafe extern "C" { pub fn ceil(n: f64) -> f64; pub fn copysign(x: f64, y: f64) -> f64; pub fn fabs(n: f64) -> f64; - // pub fn fdim(a: f64, b: f64) -> f64; pub fn fdimf(a: f32, b: f32) -> f32; pub fn floor(n: f64) -> f64; pub fn fmod(x: f64, y: f64) -> f64; @@ -61,38 +62,7 @@ unsafe extern "C" { pub fn erfc(n: f64) -> f64; pub fn erff(n: f32) -> f32; pub fn erfcf(n: f32) -> f32; - // pub fn lgamma_r(n: f64, s: &mut i32) -> f64; #[cfg(not(target_os = "aix"))] pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; - // pub fn tgamma(n: f64) -> f64; pub fn tgammaf(n: f32) -> f32; - - // pub fn acosf128(n: f128) -> f128; - // pub fn asinf128(n: f128) -> f128; - // pub fn atanf128(n: f128) -> f128; - // pub fn atan2f128(a: f128, b: f128) -> f128; - // pub fn cbrtf128(n: f128) -> f128; - // pub fn coshf128(n: f128) -> f128; - // pub fn expm1f128(n: f128) -> f128; - // pub fn hypotf128(x: f128, y: f128) -> f128; - // pub fn log1pf128(n: f128) -> f128; - // pub fn sinhf128(n: f128) -> f128; - // pub fn tanf128(n: f128) -> f128; - // pub fn tanhf128(n: f128) -> f128; - // pub fn tgammaf128(n: f128) -> f128; - // pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; - // pub fn erff128(n: f128) -> f128; - // pub fn erfcf128(n: f128) -> f128; - - // cfg_if::cfg_if! { - // if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { - // pub fn acosf(n: f32) -> f32; - // pub fn asinf(n: f32) -> f32; - // pub fn atan2f(a: f32, b: f32) -> f32; - // pub fn atanf(n: f32) -> f32; - // pub fn coshf(n: f32) -> f32; - // pub fn sinhf(n: f32) -> f32; - // pub fn tanf(n: f32) -> f32; - // pub fn tanhf(n: f32) -> f32; - // }} }