Skip to content

Commit eebf756

Browse files
committed
Implements num_traits::real::Real for NotNan<T> when T is Real and FloatCore
Forwards the calls of Real trait methods to the underlying type, panicking when the result is NaN, as suggested in https://docs.rs/num-traits/latest/num_traits/real/trait.Real.html Also adds a few tests to ensure things actually panic when they should.
1 parent abc0019 commit eebf756

File tree

2 files changed

+293
-0
lines changed

2 files changed

+293
-0
lines changed

src/lib.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use core::ops::{
2424
use core::str::FromStr;
2525

2626
pub use num_traits::float::FloatCore;
27+
use num_traits::real::Real;
2728
use num_traits::{
2829
AsPrimitive, Bounded, FloatConst, FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero,
2930
};
@@ -1957,6 +1958,170 @@ impl<T: FloatCore> NumCast for NotNan<T> {
19571958
}
19581959
}
19591960

1961+
#[cfg(any(feature = "std", feature = "libm"))]
1962+
impl<T: Real + FloatCore> Real for NotNan<T> {
1963+
fn min_value() -> Self {
1964+
NotNan(<T as Real>::min_value())
1965+
}
1966+
fn min_positive_value() -> Self {
1967+
NotNan(<T as Real>::min_positive_value())
1968+
}
1969+
fn epsilon() -> Self {
1970+
NotNan(Real::epsilon())
1971+
}
1972+
fn max_value() -> Self {
1973+
NotNan(<T as Real>::max_value())
1974+
}
1975+
fn floor(self) -> Self {
1976+
NotNan(Real::floor(self.0))
1977+
}
1978+
fn ceil(self) -> Self {
1979+
NotNan(Real::ceil(self.0))
1980+
}
1981+
fn round(self) -> Self {
1982+
NotNan(Real::round(self.0))
1983+
}
1984+
fn trunc(self) -> Self {
1985+
NotNan(Real::trunc(self.0))
1986+
}
1987+
fn fract(self) -> Self {
1988+
NotNan(Real::fract(self.0))
1989+
}
1990+
fn abs(self) -> Self {
1991+
NotNan(Real::abs(self.0))
1992+
}
1993+
fn signum(self) -> Self {
1994+
NotNan(Real::signum(self.0))
1995+
}
1996+
fn is_sign_positive(self) -> bool {
1997+
Real::is_sign_positive(self.0)
1998+
}
1999+
fn is_sign_negative(self) -> bool {
2000+
Real::is_sign_negative(self.0)
2001+
}
2002+
fn mul_add(self, a: Self, b: Self) -> Self {
2003+
NotNan(self.0.mul_add(a.0, b.0))
2004+
}
2005+
fn recip(self) -> Self {
2006+
NotNan(Real::recip(self.0))
2007+
}
2008+
fn powi(self, n: i32) -> Self {
2009+
NotNan(Real::powi(self.0, n))
2010+
}
2011+
fn powf(self, n: Self) -> Self {
2012+
// Panics if self < 0 and n is not an integer
2013+
NotNan::new(self.0.powf(n.0)).expect("Power resulted in NaN")
2014+
}
2015+
fn sqrt(self) -> Self {
2016+
// Panics if self < 0
2017+
NotNan::new(self.0.sqrt()).expect("Square root resulted in NaN")
2018+
}
2019+
fn exp(self) -> Self {
2020+
NotNan(self.0.exp())
2021+
}
2022+
fn exp2(self) -> Self {
2023+
NotNan(self.0.exp2())
2024+
}
2025+
fn ln(self) -> Self {
2026+
// Panics if self <= 0
2027+
NotNan::new(self.0.ln()).expect("Natural logarithm resulted in NaN")
2028+
}
2029+
fn log(self, base: Self) -> Self {
2030+
// Panics if self <= 0 or base <= 0
2031+
NotNan::new(self.0.log(base.0)).expect("Logarithm resulted in NaN")
2032+
}
2033+
fn log2(self) -> Self {
2034+
// Panics if self <= 0
2035+
NotNan::new(self.0.log2()).expect("Logarithm resulted in NaN")
2036+
}
2037+
fn log10(self) -> Self {
2038+
// Panics if self <= 0
2039+
NotNan::new(self.0.log10()).expect("Logarithm resulted in NaN")
2040+
}
2041+
fn to_degrees(self) -> Self {
2042+
NotNan(Real::to_degrees(self.0))
2043+
}
2044+
fn to_radians(self) -> Self {
2045+
NotNan(Real::to_radians(self.0))
2046+
}
2047+
fn max(self, other: Self) -> Self {
2048+
NotNan(Real::max(self.0, other.0))
2049+
}
2050+
fn min(self, other: Self) -> Self {
2051+
NotNan(Real::min(self.0, other.0))
2052+
}
2053+
fn abs_sub(self, other: Self) -> Self {
2054+
NotNan(self.0.abs_sub(other.0))
2055+
}
2056+
fn cbrt(self) -> Self {
2057+
NotNan(self.0.cbrt())
2058+
}
2059+
fn hypot(self, other: Self) -> Self {
2060+
NotNan(self.0.hypot(other.0))
2061+
}
2062+
fn sin(self) -> Self {
2063+
// Panics if self is +/-infinity
2064+
NotNan::new(self.0.sin()).expect("Sine resulted in NaN")
2065+
}
2066+
fn cos(self) -> Self {
2067+
// Panics if self is +/-infinity
2068+
NotNan::new(self.0.cos()).expect("Cosine resulted in NaN")
2069+
}
2070+
fn tan(self) -> Self {
2071+
// Panics if self is +/-infinity or self == pi/2 + k*pi
2072+
NotNan::new(self.0.tan()).expect("Tangent resulted in NaN")
2073+
}
2074+
fn asin(self) -> Self {
2075+
// Panics if self < -1.0 or self > 1.0
2076+
NotNan::new(self.0.asin()).expect("Arcsine resulted in NaN")
2077+
}
2078+
fn acos(self) -> Self {
2079+
// Panics if self < -1.0 or self > 1.0
2080+
NotNan::new(self.0.acos()).expect("Arccosine resulted in NaN")
2081+
}
2082+
fn atan(self) -> Self {
2083+
NotNan(self.0.atan())
2084+
}
2085+
fn atan2(self, other: Self) -> Self {
2086+
NotNan(self.0.atan2(other.0))
2087+
}
2088+
fn sin_cos(self) -> (Self, Self) {
2089+
// Panics if self is +/-infinity
2090+
let (a, b) = self.0.sin_cos();
2091+
(
2092+
NotNan::new(a).expect("Sine resulted in NaN"),
2093+
NotNan::new(b).expect("Cosine resulted in NaN"),
2094+
)
2095+
}
2096+
fn exp_m1(self) -> Self {
2097+
NotNan(self.0.exp_m1())
2098+
}
2099+
fn ln_1p(self) -> Self {
2100+
// Panics if self <= -1.0
2101+
NotNan::new(self.0.ln_1p()).expect("Natural logarithm resulted in NaN")
2102+
}
2103+
fn sinh(self) -> Self {
2104+
NotNan(self.0.sinh())
2105+
}
2106+
fn cosh(self) -> Self {
2107+
NotNan(self.0.cosh())
2108+
}
2109+
fn tanh(self) -> Self {
2110+
NotNan(self.0.tanh())
2111+
}
2112+
fn asinh(self) -> Self {
2113+
NotNan(self.0.asinh())
2114+
}
2115+
fn acosh(self) -> Self {
2116+
// Panics if self < 1.0
2117+
NotNan::new(self.0.acosh()).expect("Arccosh resulted in NaN")
2118+
}
2119+
fn atanh(self) -> Self {
2120+
// Panics if self < -1.0 or self > 1.0
2121+
NotNan::new(self.0.atanh()).expect("Arctanh resulted in NaN")
2122+
}
2123+
}
2124+
19602125
macro_rules! impl_float_const_method {
19612126
($wrapper:expr, $method:ident) => {
19622127
#[allow(non_snake_case)]

tests/test.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,134 @@ fn test_ref_ref_binop_regression() {
772772
assert_eq!(&x - &y, OrderedFloat(10.0));
773773
}
774774

775+
#[cfg(any(feature = "std", feature = "libm"))]
776+
#[test]
777+
#[should_panic]
778+
fn test_powf_fails_on_negative() {
779+
use num_traits::real::Real;
780+
Real::powf(not_nan(-1.0), not_nan(-1.5));
781+
}
782+
783+
#[cfg(any(feature = "std", feature = "libm"))]
784+
#[test]
785+
#[should_panic]
786+
fn test_sqrt_fails_on_negative() {
787+
use num_traits::real::Real;
788+
Real::sqrt(not_nan(-1.0));
789+
}
790+
791+
#[cfg(any(feature = "std", feature = "libm"))]
792+
#[test]
793+
#[should_panic]
794+
fn test_ln_fails_on_negative() {
795+
use num_traits::real::Real;
796+
Real::ln(not_nan(-1.0));
797+
}
798+
799+
#[cfg(any(feature = "std", feature = "libm"))]
800+
#[test]
801+
#[should_panic]
802+
fn test_log_fails_on_negative() {
803+
use num_traits::real::Real;
804+
Real::log(not_nan(-1.0), not_nan(2.0));
805+
}
806+
807+
#[cfg(any(feature = "std", feature = "libm"))]
808+
#[test]
809+
#[should_panic]
810+
fn test_log_fails_on_negative_base() {
811+
use num_traits::real::Real;
812+
Real::log(not_nan(1.0), not_nan(-2.0));
813+
}
814+
815+
#[cfg(any(feature = "std", feature = "libm"))]
816+
#[test]
817+
#[should_panic]
818+
fn test_log2_fails_on_negative() {
819+
use num_traits::real::Real;
820+
Real::log2(not_nan(-1.0));
821+
}
822+
823+
#[cfg(any(feature = "std", feature = "libm"))]
824+
#[test]
825+
#[should_panic]
826+
fn test_log10_fails_on_negative() {
827+
use num_traits::real::Real;
828+
Real::log10(not_nan(-1.0));
829+
}
830+
831+
#[cfg(any(feature = "std", feature = "libm"))]
832+
#[test]
833+
#[should_panic]
834+
fn test_sin_fails_on_infinite() {
835+
use num_traits::real::Real;
836+
Real::sin(not_nan(f64::INFINITY));
837+
}
838+
839+
#[cfg(any(feature = "std", feature = "libm"))]
840+
#[test]
841+
#[should_panic]
842+
fn test_cos_fails_on_infinite() {
843+
use num_traits::real::Real;
844+
Real::cos(not_nan(f64::INFINITY));
845+
}
846+
847+
#[cfg(any(feature = "std", feature = "libm"))]
848+
#[test]
849+
#[should_panic]
850+
fn test_tan_fails_on_infinite() {
851+
use num_traits::real::Real;
852+
Real::tan(not_nan(f64::INFINITY));
853+
}
854+
855+
#[cfg(any(feature = "std", feature = "libm"))]
856+
#[test]
857+
#[should_panic]
858+
fn test_asin_fails_on_big() {
859+
use num_traits::real::Real;
860+
Real::asin(not_nan(10.0));
861+
}
862+
863+
#[cfg(any(feature = "std", feature = "libm"))]
864+
#[test]
865+
#[should_panic]
866+
fn test_acos_fails_on_big() {
867+
use num_traits::real::Real;
868+
Real::acos(not_nan(10.0));
869+
}
870+
871+
#[cfg(any(feature = "std", feature = "libm"))]
872+
#[test]
873+
#[should_panic]
874+
fn test_sin_cos_fails_on_infinite() {
875+
use num_traits::real::Real;
876+
Real::sin_cos(not_nan(f64::INFINITY));
877+
}
878+
879+
#[cfg(any(feature = "std", feature = "libm"))]
880+
#[test]
881+
#[should_panic]
882+
fn test_ln_1p_fails_on_negative() {
883+
use num_traits::real::Real;
884+
Real::ln_1p(not_nan(-1.1));
885+
}
886+
887+
#[cfg(any(feature = "std", feature = "libm"))]
888+
#[test]
889+
#[should_panic]
890+
fn test_acosh_fails_on_zero() {
891+
use num_traits::real::Real;
892+
Real::acosh(not_nan(-0.0));
893+
}
894+
895+
#[cfg(any(feature = "std", feature = "libm"))]
896+
#[test]
897+
#[should_panic]
898+
fn test_atanh_fails_on_big() {
899+
use num_traits::real::Real;
900+
Real::atanh(not_nan(10.0));
901+
}
902+
775903
#[cfg(feature = "arbitrary")]
776904
mod arbitrary_test {
777905
use super::{NotNan, OrderedFloat};

0 commit comments

Comments
 (0)