Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Every function produces identical results to Python at the binary representation

## Compatibility Status

### math (56/56)
### math (61/61)

- [x] `acos`
- [x] `acosh`
Expand All @@ -38,6 +38,8 @@ Every function produces identical results to Python at the binary representation
- [x] `fabs`
- [x] `floor`
- [x] `fma`
- [x] `fmax`
- [x] `fmin`
- [x] `fmod`
- [x] `frexp`
- [x] `fsum`
Expand All @@ -48,6 +50,8 @@ Every function produces identical results to Python at the binary representation
- [x] `isfinite`
- [x] `isinf`
- [x] `isnan`
- [x] `isnormal`
- [x] `issubnormal`
- [x] `ldexp`
- [x] `lgamma`
- [x] `log`
Expand All @@ -63,6 +67,7 @@ Every function produces identical results to Python at the binary representation
- [x] `radians`
- [x] `remainder`
- [x] `sin`
- [x] `signbit`
- [x] `sinh`
- [x] `sqrt`
- [x] `sumprod` (`sumprod`, `sumprod_int`)
Expand Down
10 changes: 10 additions & 0 deletions src/m.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ pub fn floor(n: f64) -> f64 {
unsafe { m_sys::floor(n) }
}

#[inline(always)]
pub fn fmax(x: f64, y: f64) -> f64 {
unsafe { m_sys::fmax(x, y) }
}

#[inline(always)]
pub fn fmin(x: f64, y: f64) -> f64 {
unsafe { m_sys::fmin(x, y) }
}

#[inline(always)]
pub fn fmod(x: f64, y: f64) -> f64 {
unsafe { m_sys::fmod(x, y) }
Expand Down
10 changes: 10 additions & 0 deletions src/m_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,16 @@ pub fn floor(n: f64) -> f64 {
libm::floor(n)
}

#[inline(always)]
pub fn fmax(x: f64, y: f64) -> f64 {
libm::fmax(x, y)
}

#[inline(always)]
pub fn fmin(x: f64, y: f64) -> f64 {
libm::fmin(x, y)
}

#[inline(always)]
pub fn fmod(x: f64, y: f64) -> f64 {
libm::fmod(x, y)
Expand Down
2 changes: 2 additions & 0 deletions src/m_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ unsafe extern "C" {
// pub fn fdim(a: f64, b: f64) -> f64;
pub fn fdimf(a: f32, b: f32) -> f32;
pub fn floor(n: f64) -> f64;
pub fn fmax(x: f64, y: f64) -> f64;
pub fn fmin(x: f64, y: f64) -> f64;
pub fn fmod(x: f64, y: f64) -> f64;
pub fn frexp(n: f64, exp: *mut i32) -> f64;
pub fn ldexp(x: f64, n: i32) -> f64;
Expand Down
4 changes: 2 additions & 2 deletions src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pub use bigint::{comb_bigint, ldexp_bigint, log_bigint, log2_bigint, log10_bigin
pub use exponential::{cbrt, exp, exp2, expm1, log, log1p, log2, log10, pow, sqrt};
pub use gamma::{erf, erfc, gamma, lgamma};
pub use misc::{
ceil, copysign, fabs, floor, fma, fmod, frexp, isclose, isfinite, isinf, isnan, ldexp, modf,
nextafter, remainder, trunc, ulp,
ceil, copysign, fabs, floor, fma, fmax, fmin, fmod, frexp, isclose, isfinite, isinf, isnan,
isnormal, issubnormal, ldexp, modf, nextafter, remainder, signbit, trunc, ulp,
};
pub use trigonometric::{
acos, acosh, asin, asinh, atan, atan2, atanh, cos, cosh, sin, sinh, tan, tanh,
Expand Down
72 changes: 72 additions & 0 deletions src/math/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::{Error, Result, m};

super::libm_simple!(@1 ceil, floor, trunc);
super::libm_simple!(@2 fmax, fmin);

/// Return the next floating-point value after x towards y.
///
Expand Down Expand Up @@ -104,6 +105,24 @@ pub fn isnan(x: f64) -> bool {
x.is_nan()
}

/// Return True if x is normal, False otherwise.
#[inline]
pub fn isnormal(x: f64) -> bool {
x.is_normal()
}

/// Return True if x is subnormal, False otherwise.
#[inline]
pub fn issubnormal(x: f64) -> bool {
x.is_subnormal()
}

/// Return True if the sign of x is negative, False otherwise.
#[inline]
pub fn signbit(x: f64) -> bool {
x.is_sign_negative()
}

/// Return True if a and b are close to each other.
///
/// Whether or not two values are considered close is determined according to
Expand Down Expand Up @@ -259,6 +278,59 @@ pub fn ulp(x: f64) -> f64 {
mod tests {
use super::*;

#[test]
fn edgetest_python315_classification() {
for &x in crate::test::EDGE_VALUES {
let bits = x.to_bits();
let exponent = (bits >> 52) & 0x7ff;
let fraction = bits & ((1_u64 << 52) - 1);
assert_eq!(isnormal(x), exponent != 0 && exponent != 0x7ff);
assert_eq!(issubnormal(x), exponent == 0 && fraction != 0);
assert_eq!(signbit(x), bits >> 63 != 0);
}
}

#[test]
fn edgetest_python315_extrema() {
for &x in crate::test::EDGE_VALUES {
for &y in crate::test::EDGE_VALUES {
for (result, maximum) in [(fmax(x, y), true), (fmin(x, y), false)] {
// Observe the computed value: LLVM can infer a non-NaN result
// from one numeric operand, but native extrema may quiet an
// sNaN to NaN (notably in optimized AArch64 builds).
let result = std::hint::black_box(result);
if x.is_nan() && y.is_nan() {
assert!(result.is_nan());
} else if x.is_nan() || y.is_nan() {
// Signaling NaNs can be quieted by the platform libm.
let signaling = |v: f64| v.is_nan() && v.to_bits() & (1_u64 << 51) == 0;
let expected = if x.is_nan() { y } else { x };
if !signaling(x) && !signaling(y) {
assert_eq!(result.to_bits(), expected.to_bits());
} else {
assert!(
result.is_nan() || result.to_bits() == expected.to_bits(),
"f{}(0x{:016x}, 0x{:016x}) = 0x{:016x}; expected NaN or 0x{:016x}",
if maximum { "max" } else { "min" },
x.to_bits(),
y.to_bits(),
result.to_bits(),
expected.to_bits(),
);
}
} else {
let expected = if (x > y) == maximum { x } else { y };
// The sign of a zero tie is platform-dependent.
assert_eq!(result, expected);
if x != y {
assert_eq!(result.to_bits(), expected.to_bits());
}
}
}
}
}
}

/// Edge integer values for testing functions like ldexp
const EDGE_INTS: &[i32] = &[0, 1, -1, 100, -100, 1024, -1024, i32::MAX, i32::MIN];

Expand Down
Loading