From c848562fcff82e3a05c2066f9bc0406fdb97bc6c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 14:31:09 -0800 Subject: [PATCH 01/15] Use forwarding macros to implement Float and Real --- src/float.rs | 289 +++++++++----------------------------------------- src/lib.rs | 3 + src/macros.rs | 25 +++++ src/real.rs | 188 +++++++++----------------------- 4 files changed, 127 insertions(+), 378 deletions(-) create mode 100644 src/macros.rs diff --git a/src/float.rs b/src/float.rs index 5ca80d2..d09bef7 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1197,255 +1197,66 @@ macro_rules! float_impl { ::std::$T::MAX } - #[inline] - fn is_nan(self) -> bool { - <$T>::is_nan(self) - } - - #[inline] - fn is_infinite(self) -> bool { - <$T>::is_infinite(self) - } - - #[inline] - fn is_finite(self) -> bool { - <$T>::is_finite(self) - } - - #[inline] - fn is_normal(self) -> bool { - <$T>::is_normal(self) - } - - #[inline] - fn classify(self) -> FpCategory { - <$T>::classify(self) - } - - #[inline] - fn floor(self) -> Self { - <$T>::floor(self) - } - - #[inline] - fn ceil(self) -> Self { - <$T>::ceil(self) - } - - #[inline] - fn round(self) -> Self { - <$T>::round(self) - } - - #[inline] - fn trunc(self) -> Self { - <$T>::trunc(self) - } - - #[inline] - fn fract(self) -> Self { - <$T>::fract(self) - } - - #[inline] - fn abs(self) -> Self { - <$T>::abs(self) - } - - #[inline] - fn signum(self) -> Self { - <$T>::signum(self) - } - - #[inline] - fn is_sign_positive(self) -> bool { - <$T>::is_sign_positive(self) - } - - #[inline] - fn is_sign_negative(self) -> bool { - <$T>::is_sign_negative(self) - } - - #[inline] - fn mul_add(self, a: Self, b: Self) -> Self { - <$T>::mul_add(self, a, b) - } - - #[inline] - fn recip(self) -> Self { - <$T>::recip(self) - } - - #[inline] - fn powi(self, n: i32) -> Self { - <$T>::powi(self, n) - } - - #[inline] - fn powf(self, n: Self) -> Self { - <$T>::powf(self, n) - } - - #[inline] - fn sqrt(self) -> Self { - <$T>::sqrt(self) - } - - #[inline] - fn exp(self) -> Self { - <$T>::exp(self) - } - - #[inline] - fn exp2(self) -> Self { - <$T>::exp2(self) - } - - #[inline] - fn ln(self) -> Self { - <$T>::ln(self) - } - - #[inline] - fn log(self, base: Self) -> Self { - <$T>::log(self, base) - } - - #[inline] - fn log2(self) -> Self { - <$T>::log2(self) - } - - #[inline] - fn log10(self) -> Self { - <$T>::log10(self) - } - - #[inline] - fn to_degrees(self) -> Self { - // NB: `f32` didn't stabilize this until 1.7 - // <$T>::to_degrees(self) - self * (180. / ::std::$T::consts::PI) - } - - #[inline] - fn to_radians(self) -> Self { - // NB: `f32` didn't stabilize this until 1.7 - // <$T>::to_radians(self) - self * (::std::$T::consts::PI / 180.) - } - - #[inline] - fn max(self, other: Self) -> Self { - <$T>::max(self, other) - } - - #[inline] - fn min(self, other: Self) -> Self { - <$T>::min(self, other) - } - #[inline] #[allow(deprecated)] fn abs_sub(self, other: Self) -> Self { <$T>::abs_sub(self, other) } - #[inline] - fn cbrt(self) -> Self { - <$T>::cbrt(self) - } - - #[inline] - fn hypot(self, other: Self) -> Self { - <$T>::hypot(self, other) - } - - #[inline] - fn sin(self) -> Self { - <$T>::sin(self) - } - - #[inline] - fn cos(self) -> Self { - <$T>::cos(self) - } - - #[inline] - fn tan(self) -> Self { - <$T>::tan(self) - } - - #[inline] - fn asin(self) -> Self { - <$T>::asin(self) - } - - #[inline] - fn acos(self) -> Self { - <$T>::acos(self) - } - - #[inline] - fn atan(self) -> Self { - <$T>::atan(self) - } - - #[inline] - fn atan2(self, other: Self) -> Self { - <$T>::atan2(self, other) - } - - #[inline] - fn sin_cos(self) -> (Self, Self) { - <$T>::sin_cos(self) - } - - #[inline] - fn exp_m1(self) -> Self { - <$T>::exp_m1(self) - } - - #[inline] - fn ln_1p(self) -> Self { - <$T>::ln_1p(self) - } - - #[inline] - fn sinh(self) -> Self { - <$T>::sinh(self) - } - - #[inline] - fn cosh(self) -> Self { - <$T>::cosh(self) - } - - #[inline] - fn tanh(self) -> Self { - <$T>::tanh(self) - } - - #[inline] - fn asinh(self) -> Self { - <$T>::asinh(self) - } - - #[inline] - fn acosh(self) -> Self { - <$T>::acosh(self) - } - - #[inline] - fn atanh(self) -> Self { - <$T>::atanh(self) - } - #[inline] fn integer_decode(self) -> (u64, i16, i8) { $decode(self) } + + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::classify(self) -> FpCategory; + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::mul_add(self, a: Self, b: Self) -> Self; + Self::recip(self) -> Self; + Self::powi(self, n: i32) -> Self; + Self::powf(self, n: Self) -> Self; + Self::sqrt(self) -> Self; + Self::exp(self) -> Self; + Self::exp2(self) -> Self; + Self::ln(self) -> Self; + Self::log(self, base: Self) -> Self; + Self::log2(self) -> Self; + Self::log10(self) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + Self::max(self, other: Self) -> Self; + Self::min(self, other: Self) -> Self; + Self::cbrt(self) -> Self; + Self::hypot(self, other: Self) -> Self; + Self::sin(self) -> Self; + Self::cos(self) -> Self; + Self::tan(self) -> Self; + Self::asin(self) -> Self; + Self::acos(self) -> Self; + Self::atan(self) -> Self; + Self::atan2(self, other: Self) -> Self; + Self::sin_cos(self) -> (Self, Self); + Self::exp_m1(self) -> Self; + Self::ln_1p(self) -> Self; + Self::sinh(self) -> Self; + Self::cosh(self) -> Self; + Self::tanh(self) -> Self; + Self::asinh(self) -> Self; + Self::acosh(self) -> Self; + Self::atanh(self) -> Self; + } } ) } diff --git a/src/lib.rs b/src/lib.rs index 14bb3d3..3e1c80c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ pub use cast::{AsPrimitive, FromPrimitive, ToPrimitive, NumCast, cast}; pub use int::PrimInt; pub use pow::{Pow, pow, checked_pow}; +#[macro_use] +mod macros; + pub mod identities; pub mod sign; pub mod ops; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..8c7a6f8 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,25 @@ + +/// Forward a method to an inherent method or a base trait method. +macro_rules! forward { + ($( Self :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method(self $( , $arg : $ty )* ) -> $ret { + Self::$method(self $( , $arg )* ) + } + )*}; + ($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method(self $( , $arg : $ty )* ) -> $ret { + ::$method(self $( , $arg )* ) + } + )*}; + ($( $base:ident :: $method:ident ( $( $arg:ident : $ty:ty ),* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method( $( $arg : $ty ),* ) -> $ret { + ::$method( $( $arg ),* ) + } + )*} +} diff --git a/src/real.rs b/src/real.rs index ab29d45..fb6714a 100644 --- a/src/real.rs +++ b/src/real.rs @@ -782,145 +782,55 @@ pub trait Real } impl Real for T { - fn min_value() -> Self { - Self::min_value() + forward! { + Float::min_value() -> Self; + Float::min_positive_value() -> Self; + Float::epsilon() -> Self; + Float::max_value() -> Self; } - fn min_positive_value() -> Self { - Self::min_positive_value() - } - fn epsilon() -> Self { - Self::epsilon() - } - fn max_value() -> Self { - Self::max_value() - } - fn floor(self) -> Self { - self.floor() - } - fn ceil(self) -> Self { - self.ceil() - } - fn round(self) -> Self { - self.round() - } - fn trunc(self) -> Self { - self.trunc() - } - fn fract(self) -> Self { - self.fract() - } - fn abs(self) -> Self { - self.abs() - } - fn signum(self) -> Self { - self.signum() - } - fn is_sign_positive(self) -> bool { - self.is_sign_positive() - } - fn is_sign_negative(self) -> bool { - self.is_sign_negative() - } - fn mul_add(self, a: Self, b: Self) -> Self { - self.mul_add(a, b) - } - fn recip(self) -> Self { - self.recip() - } - fn powi(self, n: i32) -> Self { - self.powi(n) - } - fn powf(self, n: Self) -> Self { - self.powf(n) - } - fn sqrt(self) -> Self { - self.sqrt() - } - fn exp(self) -> Self { - self.exp() - } - fn exp2(self) -> Self { - self.exp2() - } - fn ln(self) -> Self { - self.ln() - } - fn log(self, base: Self) -> Self { - self.log(base) - } - fn log2(self) -> Self { - self.log2() - } - fn log10(self) -> Self { - self.log10() - } - fn to_degrees(self) -> Self { - self.to_degrees() - } - fn to_radians(self) -> Self { - self.to_radians() - } - fn max(self, other: Self) -> Self { - self.max(other) - } - fn min(self, other: Self) -> Self { - self.min(other) - } - fn abs_sub(self, other: Self) -> Self { - self.abs_sub(other) - } - fn cbrt(self) -> Self { - self.cbrt() - } - fn hypot(self, other: Self) -> Self { - self.hypot(other) - } - fn sin(self) -> Self { - self.sin() - } - fn cos(self) -> Self { - self.cos() - } - fn tan(self) -> Self { - self.tan() - } - fn asin(self) -> Self { - self.asin() - } - fn acos(self) -> Self { - self.acos() - } - fn atan(self) -> Self { - self.atan() - } - fn atan2(self, other: Self) -> Self { - self.atan2(other) - } - fn sin_cos(self) -> (Self, Self) { - self.sin_cos() - } - fn exp_m1(self) -> Self { - self.exp_m1() - } - fn ln_1p(self) -> Self { - self.ln_1p() - } - fn sinh(self) -> Self { - self.sinh() - } - fn cosh(self) -> Self { - self.cosh() - } - fn tanh(self) -> Self { - self.tanh() - } - fn asinh(self) -> Self { - self.asinh() - } - fn acosh(self) -> Self { - self.acosh() - } - fn atanh(self) -> Self { - self.atanh() + forward! { + Float::floor(self) -> Self; + Float::ceil(self) -> Self; + Float::round(self) -> Self; + Float::trunc(self) -> Self; + Float::fract(self) -> Self; + Float::abs(self) -> Self; + Float::signum(self) -> Self; + Float::is_sign_positive(self) -> bool; + Float::is_sign_negative(self) -> bool; + Float::mul_add(self, a: Self, b: Self) -> Self; + Float::recip(self) -> Self; + Float::powi(self, n: i32) -> Self; + Float::powf(self, n: Self) -> Self; + Float::sqrt(self) -> Self; + Float::exp(self) -> Self; + Float::exp2(self) -> Self; + Float::ln(self) -> Self; + Float::log(self, base: Self) -> Self; + Float::log2(self) -> Self; + Float::log10(self) -> Self; + Float::to_degrees(self) -> Self; + Float::to_radians(self) -> Self; + Float::max(self, other: Self) -> Self; + Float::min(self, other: Self) -> Self; + Float::abs_sub(self, other: Self) -> Self; + Float::cbrt(self) -> Self; + Float::hypot(self, other: Self) -> Self; + Float::sin(self) -> Self; + Float::cos(self) -> Self; + Float::tan(self) -> Self; + Float::asin(self) -> Self; + Float::acos(self) -> Self; + Float::atan(self) -> Self; + Float::atan2(self, other: Self) -> Self; + Float::sin_cos(self) -> (Self, Self); + Float::exp_m1(self) -> Self; + Float::ln_1p(self) -> Self; + Float::sinh(self) -> Self; + Float::cosh(self) -> Self; + Float::tanh(self) -> Self; + Float::asinh(self) -> Self; + Float::acosh(self) -> Self; + Float::atanh(self) -> Self; } } From ac503261ca1d82457a5a3a72332c050cf4eb8369 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 14:41:09 -0800 Subject: [PATCH 02/15] Forward FloatCore to inherent methods when possible --- src/cast.rs | 1 - src/float.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/cast.rs b/src/cast.rs index 3d7d0a6..630341f 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -1,4 +1,3 @@ -use core::f64; use core::mem::size_of; use core::num::Wrapping; diff --git a/src/float.rs b/src/float.rs index d09bef7..c2e28d4 100644 --- a/src/float.rs +++ b/src/float.rs @@ -166,6 +166,7 @@ impl FloatCore for f32 { } #[inline] + #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { const EXP_MASK: u32 = 0x7f800000; const MAN_MASK: u32 = 0x007fffff; @@ -181,14 +182,35 @@ impl FloatCore for f32 { } #[inline] + #[cfg(not(feature = "std"))] fn to_degrees(self) -> Self { self * (180.0 / ::core::f32::consts::PI) } #[inline] + #[cfg(not(feature = "std"))] fn to_radians(self) -> Self { self * (::core::f32::consts::PI / 180.0) } + + #[cfg(feature = "std")] + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::classify(self) -> FpCategory; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::min(self, other: Self) -> Self; + Self::max(self, other: Self) -> Self; + Self::recip(self) -> Self; + Self::powi(self, n: i32) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + } } impl FloatCore for f64 { @@ -208,6 +230,7 @@ impl FloatCore for f64 { } #[inline] + #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { const EXP_MASK: u64 = 0x7ff0000000000000; const MAN_MASK: u64 = 0x000fffffffffffff; @@ -223,14 +246,35 @@ impl FloatCore for f64 { } #[inline] + #[cfg(not(feature = "std"))] fn to_degrees(self) -> Self { self * (180.0 / ::core::f64::consts::PI) } #[inline] + #[cfg(not(feature = "std"))] fn to_radians(self) -> Self { self * (::core::f64::consts::PI / 180.0) } + + #[cfg(feature = "std")] + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::classify(self) -> FpCategory; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::min(self, other: Self) -> Self; + Self::max(self, other: Self) -> Self; + Self::recip(self) -> Self; + Self::powi(self, n: i32) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + } } // FIXME: these doctests aren't actually helpful, because they're using and From 7d6575da0fa421d1baa3482a30e6408505316b8e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 14:43:31 -0800 Subject: [PATCH 03/15] Add NumCast to FloatCore, matching Float --- src/float.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/float.rs b/src/float.rs index c2e28d4..7952357 100644 --- a/src/float.rs +++ b/src/float.rs @@ -6,14 +6,12 @@ use core::num::FpCategory; #[cfg(feature = "std")] use std::f32; -use {Num, ToPrimitive}; -#[cfg(feature = "std")] -use NumCast; +use {Num, NumCast, ToPrimitive}; /// Generic trait for floating point numbers that works with `no_std`. /// /// This trait implements a subset of the `Float` trait. -pub trait FloatCore: Num + Neg + PartialOrd + Copy { +pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns positive infinity. fn infinity() -> Self; From 99c6cc11ba1ba6b7dfc49c7f0866f8b482dc34be Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 16:31:36 -0800 Subject: [PATCH 04/15] Add more constants to FloatCore --- src/float.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/float.rs b/src/float.rs index 7952357..7522d58 100644 --- a/src/float.rs +++ b/src/float.rs @@ -21,6 +21,21 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns NaN. fn nan() -> Self; + /// Returns `-0.0`. + fn neg_zero() -> Self; + + /// Returns the smallest finite value that this type can represent. + fn min_value() -> Self; + + /// Returns the smallest positive, normalized value that this type can represent. + fn min_positive_value() -> Self; + + /// Returns epsilon, a small positive value. + fn epsilon() -> Self; + + /// Returns the largest finite value that this type can represent. + fn max_value() -> Self; + /// Returns `true` if the number is NaN. #[inline] fn is_nan(self) -> bool { @@ -163,6 +178,31 @@ impl FloatCore for f32 { ::core::f32::NAN } + #[inline] + fn neg_zero() -> Self { + -0.0 + } + + #[inline] + fn min_value() -> Self { + ::core::f32::MIN + } + + #[inline] + fn min_positive_value() -> Self { + ::core::f32::MIN_POSITIVE + } + + #[inline] + fn epsilon() -> Self { + ::core::f32::EPSILON + } + + #[inline] + fn max_value() -> Self { + ::core::f32::MAX + } + #[inline] #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { @@ -227,6 +267,31 @@ impl FloatCore for f64 { ::core::f64::NAN } + #[inline] + fn neg_zero() -> Self { + -0.0 + } + + #[inline] + fn min_value() -> Self { + ::core::f64::MIN + } + + #[inline] + fn min_positive_value() -> Self { + ::core::f64::MIN_POSITIVE + } + + #[inline] + fn epsilon() -> Self { + ::core::f64::EPSILON + } + + #[inline] + fn max_value() -> Self { + ::core::f64::MAX + } + #[inline] #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { From f365a4205f84ff6d33fd95bd71d8b30bc38caaf7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 16:32:31 -0800 Subject: [PATCH 05/15] Add rounding methods to FloatCore --- src/float.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/float.rs b/src/float.rs index 7522d58..a75d55c 100644 --- a/src/float.rs +++ b/src/float.rs @@ -65,6 +65,76 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// predicate instead. fn classify(self) -> FpCategory; + /// Returns the largest integer less than or equal to a number. + #[inline] + fn floor(self) -> Self { + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self < Self::zero() { + self - f - Self::one() + } else { + self - f + } + } + + /// Returns the smallest integer greater than or equal to a number. + #[inline] + fn ceil(self) -> Self { + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self > Self::zero() { + self - f + Self::one() + } else { + self - f + } + } + + /// Returns the nearest integer to a number. Round half-way cases away from `0.0`. + #[inline] + fn round(self) -> Self { + let one = Self::one(); + let h = Self::from(0.5).expect("Unable to cast from 0.5"); + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self > Self::zero() { + if f < h { + self - f + } else { + self - f + one + } + } else { + if -f < h { + self - f + } else { + self - f - one + } + } + } + + /// Return the integer part of a number. + #[inline] + fn trunc(self) -> Self { + let f = self.fract(); + if f.is_nan() { + self + } else { + self - f + } + } + + /// Returns the fractional part of a number. + #[inline] + fn fract(self) -> Self { + if self.is_zero() { + Self::zero() + } else { + self % Self::one() + } + } + /// Computes the absolute value of `self`. Returns `FloatCore::nan()` if the /// number is `FloatCore::nan()`. #[inline] @@ -238,6 +308,11 @@ impl FloatCore for f32 { Self::is_finite(self) -> bool; Self::is_normal(self) -> bool; Self::classify(self) -> FpCategory; + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; Self::abs(self) -> Self; Self::signum(self) -> Self; Self::is_sign_positive(self) -> bool; @@ -327,6 +402,11 @@ impl FloatCore for f64 { Self::is_finite(self) -> bool; Self::is_normal(self) -> bool; Self::classify(self) -> FpCategory; + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; Self::abs(self) -> Self; Self::signum(self) -> Self; Self::is_sign_positive(self) -> bool; From 83d498d0be8fdb9c82afc8aad7a211965de06a15 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 16:32:48 -0800 Subject: [PATCH 06/15] Add integer_decode to FloatCore --- src/float.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/float.rs b/src/float.rs index a75d55c..0caf561 100644 --- a/src/float.rs +++ b/src/float.rs @@ -230,6 +230,11 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Converts to radians, assuming the number is in degrees. fn to_radians(self) -> Self; + + /// Returns the mantissa, base 2 exponent, and sign as integers, respectively. + /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`. + /// The floating point encoding is documented in the [Reference][floating-point]. + fn integer_decode(self) -> (u64, i16, i8); } impl FloatCore for f32 { @@ -273,6 +278,11 @@ impl FloatCore for f32 { ::core::f32::MAX } + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + integer_decode_f32(self) + } + #[inline] #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { @@ -367,6 +377,11 @@ impl FloatCore for f64 { ::core::f64::MAX } + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + integer_decode_f64(self) + } + #[inline] #[cfg(not(feature = "std"))] fn classify(self) -> FpCategory { @@ -1448,7 +1463,6 @@ macro_rules! float_impl { ) } -#[cfg(feature = "std")] fn integer_decode_f32(f: f32) -> (u64, i16, i8) { let bits: u32 = unsafe { mem::transmute(f) }; let sign: i8 = if bits >> 31 == 0 { @@ -1467,7 +1481,6 @@ fn integer_decode_f32(f: f32) -> (u64, i16, i8) { (mantissa as u64, exponent, sign) } -#[cfg(feature = "std")] fn integer_decode_f64(f: f64) -> (u64, i16, i8) { let bits: u64 = unsafe { mem::transmute(f) }; let sign: i8 = if bits >> 63 == 0 { From 6fa29be7c0ccb3d6f12b96fc79358bde01a74f46 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 16:51:30 -0800 Subject: [PATCH 07/15] Use macros for more float constants --- src/float.rs | 163 +++++++++++--------------------------------------- src/macros.rs | 10 ++++ 2 files changed, 46 insertions(+), 127 deletions(-) diff --git a/src/float.rs b/src/float.rs index 0caf561..ae8df03 100644 --- a/src/float.rs +++ b/src/float.rs @@ -2,9 +2,8 @@ use core::mem; use core::ops::Neg; use core::num::FpCategory; -// Used for default implementation of `epsilon` -#[cfg(feature = "std")] -use std::f32; +use core::f32; +use core::f64; use {Num, NumCast, ToPrimitive}; @@ -238,44 +237,15 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } impl FloatCore for f32 { - #[inline] - fn infinity() -> Self { - ::core::f32::INFINITY - } - - #[inline] - fn neg_infinity() -> Self { - ::core::f32::NEG_INFINITY - } - - #[inline] - fn nan() -> Self { - ::core::f32::NAN - } - - #[inline] - fn neg_zero() -> Self { - -0.0 - } - - #[inline] - fn min_value() -> Self { - ::core::f32::MIN - } - - #[inline] - fn min_positive_value() -> Self { - ::core::f32::MIN_POSITIVE - } - - #[inline] - fn epsilon() -> Self { - ::core::f32::EPSILON - } - - #[inline] - fn max_value() -> Self { - ::core::f32::MAX + constant! { + infinity() -> f32::INFINITY; + neg_infinity() -> f32::NEG_INFINITY; + nan() -> f32::NAN; + neg_zero() -> -0.0; + min_value() -> f32::MIN; + min_positive_value() -> f32::MIN_POSITIVE; + epsilon() -> f32::EPSILON; + max_value() -> f32::MAX; } #[inline] @@ -302,13 +272,13 @@ impl FloatCore for f32 { #[inline] #[cfg(not(feature = "std"))] fn to_degrees(self) -> Self { - self * (180.0 / ::core::f32::consts::PI) + self * (180.0 / f32::consts::PI) } #[inline] #[cfg(not(feature = "std"))] fn to_radians(self) -> Self { - self * (::core::f32::consts::PI / 180.0) + self * (f32::consts::PI / 180.0) } #[cfg(feature = "std")] @@ -337,44 +307,15 @@ impl FloatCore for f32 { } impl FloatCore for f64 { - #[inline] - fn infinity() -> Self { - ::core::f64::INFINITY - } - - #[inline] - fn neg_infinity() -> Self { - ::core::f64::NEG_INFINITY - } - - #[inline] - fn nan() -> Self { - ::core::f64::NAN - } - - #[inline] - fn neg_zero() -> Self { - -0.0 - } - - #[inline] - fn min_value() -> Self { - ::core::f64::MIN - } - - #[inline] - fn min_positive_value() -> Self { - ::core::f64::MIN_POSITIVE - } - - #[inline] - fn epsilon() -> Self { - ::core::f64::EPSILON - } - - #[inline] - fn max_value() -> Self { - ::core::f64::MAX + constant! { + infinity() -> f64::INFINITY; + neg_infinity() -> f64::NEG_INFINITY; + nan() -> f64::NAN; + neg_zero() -> -0.0; + min_value() -> f64::MIN; + min_positive_value() -> f64::MIN_POSITIVE; + epsilon() -> f64::EPSILON; + max_value() -> f64::MAX; } #[inline] @@ -401,13 +342,13 @@ impl FloatCore for f64 { #[inline] #[cfg(not(feature = "std"))] fn to_degrees(self) -> Self { - self * (180.0 / ::core::f64::consts::PI) + self * (180.0 / f64::consts::PI) } #[inline] #[cfg(not(feature = "std"))] fn to_radians(self) -> Self { - self * (::core::f64::consts::PI / 180.0) + self * (f64::consts::PI / 180.0) } #[cfg(feature = "std")] @@ -1359,44 +1300,15 @@ pub trait Float macro_rules! float_impl { ($T:ident $decode:ident) => ( impl Float for $T { - #[inline] - fn nan() -> Self { - ::std::$T::NAN - } - - #[inline] - fn infinity() -> Self { - ::std::$T::INFINITY - } - - #[inline] - fn neg_infinity() -> Self { - ::std::$T::NEG_INFINITY - } - - #[inline] - fn neg_zero() -> Self { - -0.0 - } - - #[inline] - fn min_value() -> Self { - ::std::$T::MIN - } - - #[inline] - fn min_positive_value() -> Self { - ::std::$T::MIN_POSITIVE - } - - #[inline] - fn epsilon() -> Self { - ::std::$T::EPSILON - } - - #[inline] - fn max_value() -> Self { - ::std::$T::MAX + constant! { + nan() -> $T::NAN; + infinity() -> $T::INFINITY; + neg_infinity() -> $T::NEG_INFINITY; + neg_zero() -> -0.0; + min_value() -> $T::MIN; + min_positive_value() -> $T::MIN_POSITIVE; + epsilon() -> $T::EPSILON; + max_value() -> $T::MAX; } #[inline] @@ -1515,12 +1427,9 @@ macro_rules! float_const_impl { ); (@float $T:ident, $($constant:ident,)+) => ( impl FloatConst for $T { - $( - #[inline] - fn $constant() -> Self { - ::core::$T::consts::$constant - } - )+ + constant! { + $( $constant() -> $T::consts::$constant; )+ + } } ); } diff --git a/src/macros.rs b/src/macros.rs index 8c7a6f8..a5f816e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,3 +23,13 @@ macro_rules! forward { } )*} } + +macro_rules! constant { + ($( $method:ident () -> $ret:expr ; )*) + => {$( + #[inline] + fn $method() -> Self { + $ret + } + )*}; +} From 8d16921579a4e09cfb42c41e23cfa965dd00823d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 17:08:35 -0800 Subject: [PATCH 08/15] allow unused macros --- src/macros.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index a5f816e..4330cdf 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,5 @@ +// not all are used in all features configurations +#![allow(unused)] /// Forward a method to an inherent method or a base trait method. macro_rules! forward { From 964a7e52a8437a7613194f10eabe7a6c246501b3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 21:03:30 -0800 Subject: [PATCH 09/15] Reinstate NAN-sign fixes in FloatCore Formerly changed on the next branch, part of rust-num/num#319. --- src/float.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/float.rs b/src/float.rs index ae8df03..6d1fcdc 100644 --- a/src/float.rs +++ b/src/float.rs @@ -154,27 +154,28 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// - `FloatCore::nan()` if the number is `FloatCore::nan()` #[inline] fn signum(self) -> Self { - if self.is_sign_positive() { - return Self::one(); + if self.is_nan() { + Self::nan() + } else if self.is_sign_negative() { + -Self::one() + } else { + Self::one() } - if self.is_sign_negative() { - return -Self::one(); - } - Self::nan() } /// Returns `true` if `self` is positive, including `+0.0` and /// `FloatCore::infinity()`. #[inline] fn is_sign_positive(self) -> bool { - self > Self::zero() || (Self::one() / self) == Self::infinity() + !self.is_sign_negative() } /// Returns `true` if `self` is negative, including `-0.0` and /// `FloatCore::neg_infinity()`. #[inline] fn is_sign_negative(self) -> bool { - self < Self::zero() || (Self::one() / self) == Self::neg_infinity() + let (_, _, sign) = self.integer_decode(); + sign < 0 } /// Returns the minimum of the two numbers. From 36c7e324dbdc46e5dd7e42c3b816cd68064cd092 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 21:34:01 -0800 Subject: [PATCH 10/15] Fix FloatCore::powi with i32::MIN exponent --- src/float.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/float.rs b/src/float.rs index 6d1fcdc..fdc5f7d 100644 --- a/src/float.rs +++ b/src/float.rs @@ -5,7 +5,7 @@ use core::num::FpCategory; use core::f32; use core::f64; -use {Num, NumCast, ToPrimitive}; +use {Num, NumCast}; /// Generic trait for floating point numbers that works with `no_std`. /// @@ -218,11 +218,11 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { #[inline] fn powi(mut self, mut exp: i32) -> Self { if exp < 0 { - exp = -exp; + exp = exp.wrapping_neg(); self = self.recip(); } // It should always be possible to convert a positive `i32` to a `usize`. - super::pow(self, exp.to_usize().unwrap()) + super::pow(self, exp as u32 as usize) } /// Converts to degrees, assuming the number is in radians. From ac6eca4b66e4d8dce69c6e2d8c76b7c87c682ab6 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 21:50:44 -0800 Subject: [PATCH 11/15] Use more FloatCore in src/sign.rs --- src/sign.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sign.rs b/src/sign.rs index 437c8bf..932496a 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -1,9 +1,7 @@ use core::ops::Neg; -use core::{f32, f64}; use core::num::Wrapping; use Num; -#[cfg(not(feature = "std"))] use float::FloatCore; /// Useful functions for signed numbers (i.e. numbers that can be negative). @@ -101,12 +99,12 @@ impl Signed for Wrapping where Wrapping: Num + Neg { + ($t:ty) => { impl Signed for $t { /// Computes the absolute value. Returns `NAN` if the number is `NAN`. #[inline] fn abs(&self) -> $t { - (*self).abs() + FloatCore::abs(*self) } /// The positive difference of two numbers. Returns `0.0` if the number is @@ -124,23 +122,22 @@ macro_rules! signed_float_impl { /// - `NAN` if the number is NaN #[inline] fn signum(&self) -> $t { - use float::FloatCore; FloatCore::signum(*self) } /// Returns `true` if the number is positive, including `+0.0` and `INFINITY` #[inline] - fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == $inf } + fn is_positive(&self) -> bool { FloatCore::is_sign_positive(*self) } /// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY` #[inline] - fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == $neg_inf } + fn is_negative(&self) -> bool { FloatCore::is_sign_negative(*self) } } } } -signed_float_impl!(f32, f32::NAN, f32::INFINITY, f32::NEG_INFINITY); -signed_float_impl!(f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY); +signed_float_impl!(f32); +signed_float_impl!(f64); /// Computes the absolute value. /// From ec3cd50f3d6feefb8df23906cd55e7eb909885a0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 22:10:46 -0800 Subject: [PATCH 12/15] Weaken the std f32::to_degrees/to_radians tests --- src/float.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/float.rs b/src/float.rs index fdc5f7d..d35b071 100644 --- a/src/float.rs +++ b/src/float.rs @@ -1493,8 +1493,8 @@ mod tests { assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-6); let (deg, rad) = (deg as f32, rad as f32); - assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-6); - assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-6); + assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-5); + assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-5); } } @@ -1508,8 +1508,8 @@ mod tests { assert!((Float::to_radians(deg) - rad).abs() < 1e-6); let (deg, rad) = (deg as f32, rad as f32); - assert!((Float::to_degrees(rad) - deg).abs() < 1e-6); - assert!((Float::to_radians(deg) - rad).abs() < 1e-6); + assert!((Float::to_degrees(rad) - deg).abs() < 1e-5); + assert!((Float::to_radians(deg) - rad).abs() < 1e-5); } } } From aa9ceba628f495d1b7de190fa564de43d90c8d84 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 27 Feb 2018 22:12:37 -0800 Subject: [PATCH 13/15] Add doctests to FloatCore --- src/float.rs | 504 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 499 insertions(+), 5 deletions(-) diff --git a/src/float.rs b/src/float.rs index d35b071..eeeb48b 100644 --- a/src/float.rs +++ b/src/float.rs @@ -12,48 +12,230 @@ use {Num, NumCast}; /// This trait implements a subset of the `Float` trait. pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns positive infinity. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::infinity() == x); + /// } + /// + /// check(f32::INFINITY); + /// check(f64::INFINITY); + /// ``` fn infinity() -> Self; /// Returns negative infinity. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::neg_infinity() == x); + /// } + /// + /// check(f32::NEG_INFINITY); + /// check(f64::NEG_INFINITY); + /// ``` fn neg_infinity() -> Self; /// Returns NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// + /// fn check() { + /// let n = T::nan(); + /// assert!(n != n); + /// } + /// + /// check::(); + /// check::(); + /// ``` fn nan() -> Self; /// Returns `-0.0`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(n: T) { + /// let z = T::neg_zero(); + /// assert!(z.is_zero()); + /// assert!(T::one() / z == n); + /// } + /// + /// check(f32::NEG_INFINITY); + /// check(f64::NEG_INFINITY); + /// ``` fn neg_zero() -> Self; /// Returns the smallest finite value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::min_value() == x); + /// } + /// + /// check(f32::MIN); + /// check(f64::MIN); + /// ``` fn min_value() -> Self; /// Returns the smallest positive, normalized value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::min_positive_value() == x); + /// } + /// + /// check(f32::MIN_POSITIVE); + /// check(f64::MIN_POSITIVE); + /// ``` fn min_positive_value() -> Self; /// Returns epsilon, a small positive value. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::epsilon() == x); + /// } + /// + /// check(f32::EPSILON); + /// check(f64::EPSILON); + /// ``` fn epsilon() -> Self; /// Returns the largest finite value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::max_value() == x); + /// } + /// + /// check(f32::MAX); + /// check(f64::MAX); + /// ``` fn max_value() -> Self; /// Returns `true` if the number is NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_nan() == p); + /// } + /// + /// check(f32::NAN, true); + /// check(f32::INFINITY, false); + /// check(f64::NAN, true); + /// check(0.0f64, false); + /// ``` #[inline] fn is_nan(self) -> bool { self != self } /// Returns `true` if the number is infinite. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_infinite() == p); + /// } + /// + /// check(f32::INFINITY, true); + /// check(f32::NEG_INFINITY, true); + /// check(f32::NAN, false); + /// check(f64::INFINITY, true); + /// check(f64::NEG_INFINITY, true); + /// check(0.0f64, false); + /// ``` #[inline] fn is_infinite(self) -> bool { self == Self::infinity() || self == Self::neg_infinity() } /// Returns `true` if the number is neither infinite or NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_finite() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, true); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(f64::NAN, false); + /// ``` #[inline] fn is_finite(self) -> bool { !(self.is_nan() || self.is_infinite()) } /// Returns `true` if the number is neither zero, infinite, subnormal or NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_normal() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, true); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(0.0f64, false); + /// ``` #[inline] fn is_normal(self) -> bool { self.classify() == FpCategory::Normal @@ -62,9 +244,49 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns the floating point category of the number. If only one property /// is going to be tested, it is generally faster to use the specific /// predicate instead. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// use std::num::FpCategory; + /// + /// fn check(x: T, c: FpCategory) { + /// assert!(x.classify() == c); + /// } + /// + /// check(f32::INFINITY, FpCategory::Infinite); + /// check(f32::MAX, FpCategory::Normal); + /// check(f64::NAN, FpCategory::Nan); + /// check(f64::MIN_POSITIVE, FpCategory::Normal); + /// check(f64::MIN_POSITIVE / 2.0, FpCategory::Subnormal); + /// check(0.0f64, FpCategory::Zero); + /// ``` fn classify(self) -> FpCategory; /// Returns the largest integer less than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.floor() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 0.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 1.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -1.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -2.0); + /// check(f64::MIN, f64::MIN); + /// ``` #[inline] fn floor(self) -> Self { let f = self.fract(); @@ -78,6 +300,27 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns the smallest integer greater than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.ceil() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 1.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 2.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -0.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` #[inline] fn ceil(self) -> Self { let f = self.fract(); @@ -91,6 +334,26 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns the nearest integer to a number. Round half-way cases away from `0.0`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.round() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.4f32, 0.0); + /// check(0.5f32, 1.0); + /// check(0.6f32, 1.0); + /// check(-0.4f64, 0.0); + /// check(-0.5f64, -1.0); + /// check(-0.6f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` #[inline] fn round(self) -> Self { let one = Self::one(); @@ -114,6 +377,27 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Return the integer part of a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.trunc() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 0.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 1.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -0.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` #[inline] fn trunc(self) -> Self { let f = self.fract(); @@ -125,6 +409,27 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns the fractional part of a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.fract() == y); + /// } + /// + /// check(f32::MAX, 0.0); + /// check(0.75f32, 0.75); + /// check(1.0f32, 0.0); + /// check(1.25f32, 0.25); + /// check(-0.0f64, 0.0); + /// check(-0.75f64, -0.75); + /// check(-1.0f64, 0.0); + /// check(-1.25f64, -0.25); + /// check(f64::MIN, 0.0); + /// ``` #[inline] fn fract(self) -> Self { if self.is_zero() { @@ -136,6 +441,24 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Computes the absolute value of `self`. Returns `FloatCore::nan()` if the /// number is `FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.abs() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(1.0f32, 1.0); + /// check(0.0f64, 0.0); + /// check(-0.0f64, 0.0); + /// check(-1.0f64, 1.0); + /// check(f64::MIN, f64::MAX); + /// ``` #[inline] fn abs(self) -> Self { if self.is_sign_positive() { @@ -152,6 +475,24 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// - `1.0` if the number is positive, `+0.0` or `FloatCore::infinity()` /// - `-1.0` if the number is negative, `-0.0` or `FloatCore::neg_infinity()` /// - `FloatCore::nan()` if the number is `FloatCore::nan()` + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.signum() == y); + /// } + /// + /// check(f32::INFINITY, 1.0); + /// check(3.0f32, 1.0); + /// check(0.0f32, 1.0); + /// check(-0.0f64, -1.0); + /// check(-3.0f64, -1.0); + /// check(f64::MIN, -1.0); + /// ``` #[inline] fn signum(self) -> Self { if self.is_nan() { @@ -164,14 +505,54 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns `true` if `self` is positive, including `+0.0` and - /// `FloatCore::infinity()`. + /// `FloatCore::infinity()`, and with newer versions of Rust + /// even `FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_sign_positive() == p); + /// } + /// + /// check(f32::INFINITY, true); + /// check(f32::MAX, true); + /// check(0.0f32, true); + /// check(-0.0f64, false); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(-f64::NAN, false); + /// ``` #[inline] fn is_sign_positive(self) -> bool { !self.is_sign_negative() } /// Returns `true` if `self` is negative, including `-0.0` and - /// `FloatCore::neg_infinity()`. + /// `FloatCore::neg_infinity()`, and with newer versions of Rust + /// even `-FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_sign_negative() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, false); + /// check(0.0f32, false); + /// check(-0.0f64, true); + /// check(f64::NEG_INFINITY, true); + /// check(f64::MIN_POSITIVE, false); + /// check(f64::NAN, false); + /// ``` #[inline] fn is_sign_negative(self) -> bool { let (_, _, sign) = self.integer_decode(); @@ -181,6 +562,22 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns the minimum of the two numbers. /// /// If one of the arguments is NaN, then the other argument is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T, min: T) { + /// assert!(x.min(y) == min); + /// } + /// + /// check(1.0f32, 2.0, 1.0); + /// check(f32::NAN, 2.0, 2.0); + /// check(1.0f64, -2.0, -2.0); + /// check(1.0f64, f64::NAN, 1.0); + /// ``` #[inline] fn min(self, other: Self) -> Self { if self.is_nan() { @@ -195,6 +592,22 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Returns the maximum of the two numbers. /// /// If one of the arguments is NaN, then the other argument is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T, min: T) { + /// assert!(x.max(y) == min); + /// } + /// + /// check(1.0f32, 2.0, 2.0); + /// check(1.0f32, f32::NAN, 1.0); + /// check(-1.0f64, 2.0, 2.0); + /// check(-1.0f64, f64::NAN, -1.0); + /// ``` #[inline] fn max(self, other: Self) -> Self { if self.is_nan() { @@ -207,6 +620,23 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns the reciprocal (multiplicative inverse) of the number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.recip() == y); + /// assert!(y.recip() == x); + /// } + /// + /// check(f32::INFINITY, 0.0); + /// check(2.0f32, 0.5); + /// check(-0.25f64, -4.0); + /// check(-0.0f64, f64::NEG_INFINITY); + /// ``` #[inline] fn recip(self) -> Self { Self::one() / self @@ -215,6 +645,22 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { /// Raise a number to an integer power. /// /// Using this function is generally faster than using `powf` + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// + /// fn check(x: T, exp: i32, powi: T) { + /// assert!(x.powi(exp) == powi); + /// } + /// + /// check(9.0f32, 2, 81.0); + /// check(1.0f32, -2, 1.0); + /// check(10.0f64, 20, 1e20); + /// check(4.0f64, -2, 0.0625); + /// check(-1.0f64, std::i32::MIN, 1.0); + /// ``` #[inline] fn powi(mut self, mut exp: i32) -> Self { if exp < 0 { @@ -226,14 +672,64 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Converts to degrees, assuming the number is in radians. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(rad: T, deg: T) { + /// assert!(rad.to_degrees() == deg); + /// } + /// + /// check(0.0f32, 0.0); + /// check(f32::consts::PI, 180.0); + /// check(f64::consts::FRAC_PI_4, 45.0); + /// check(f64::INFINITY, f64::INFINITY); + /// ``` fn to_degrees(self) -> Self; /// Converts to radians, assuming the number is in degrees. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(deg: T, rad: T) { + /// assert!(deg.to_radians() == rad); + /// } + /// + /// check(0.0f32, 0.0); + /// check(180.0, f32::consts::PI); + /// check(45.0, f64::consts::FRAC_PI_4); + /// check(f64::INFINITY, f64::INFINITY); + /// ``` fn to_radians(self) -> Self; /// Returns the mantissa, base 2 exponent, and sign as integers, respectively. /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`. - /// The floating point encoding is documented in the [Reference][floating-point]. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, m: u64, e: i16, s:i8) { + /// let (mantissa, exponent, sign) = x.integer_decode(); + /// assert_eq!(mantissa, m); + /// assert_eq!(exponent, e); + /// assert_eq!(sign, s); + /// } + /// + /// check(2.0f32, 1 << 23, -22, 1); + /// check(-2.0f32, 1 << 23, -22, -1); + /// check(f32::INFINITY, 1 << 23, 105, 1); + /// check(f64::NEG_INFINITY, 1 << 52, 972, -1); + /// ``` fn integer_decode(self) -> (u64, i16, i8); } @@ -1275,7 +1771,6 @@ pub trait Float /// Returns the mantissa, base 2 exponent, and sign as integers, respectively. /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`. - /// The floating point encoding is documented in the [Reference][floating-point]. /// /// ``` /// use num_traits::Float; @@ -1293,7 +1788,6 @@ pub trait Float /// /// assert!(abs_difference < 1e-10); /// ``` - /// [floating-point]: ../../../../../reference.html#machine-types fn integer_decode(self) -> (u64, i16, i8); } From 080f6f259ef9e4d935640f3d7b461326a31f3e1c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 28 Feb 2018 11:33:34 -0800 Subject: [PATCH 14/15] Comment the i32::MIN case for FloatCore::powi --- src/float.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/float.rs b/src/float.rs index eeeb48b..8f5c7f0 100644 --- a/src/float.rs +++ b/src/float.rs @@ -5,7 +5,7 @@ use core::num::FpCategory; use core::f32; use core::f64; -use {Num, NumCast}; +use {Num, NumCast, ToPrimitive}; /// Generic trait for floating point numbers that works with `no_std`. /// @@ -668,7 +668,9 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { self = self.recip(); } // It should always be possible to convert a positive `i32` to a `usize`. - super::pow(self, exp as u32 as usize) + // Note, `i32::MIN` will wrap and still be negative, so we need to convert + // to `u32` without sign-extension before growing to `usize`. + super::pow(self, (exp as u32).to_usize().unwrap()) } /// Converts to degrees, assuming the number is in radians. From 04a3f2a5914bbda644aec7f724a49d4a670d0df0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 28 Feb 2018 11:43:55 -0800 Subject: [PATCH 15/15] Comment the Rust version for NAN.is_sign_* behavior --- src/float.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/float.rs b/src/float.rs index 8f5c7f0..072a91b 100644 --- a/src/float.rs +++ b/src/float.rs @@ -505,8 +505,8 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns `true` if `self` is positive, including `+0.0` and - /// `FloatCore::infinity()`, and with newer versions of Rust - /// even `FloatCore::nan()`. + /// `FloatCore::infinity()`, and since Rust 1.20 also + /// `FloatCore::nan()`. /// /// # Examples /// @@ -532,8 +532,8 @@ pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { } /// Returns `true` if `self` is negative, including `-0.0` and - /// `FloatCore::neg_infinity()`, and with newer versions of Rust - /// even `-FloatCore::nan()`. + /// `FloatCore::neg_infinity()`, and since Rust 1.20 also + /// `-FloatCore::nan()`. /// /// # Examples /// @@ -1196,7 +1196,7 @@ pub trait Float fn signum(self) -> Self; /// Returns `true` if `self` is positive, including `+0.0`, - /// `Float::infinity()`, and with newer versions of Rust `f64::NAN`. + /// `Float::infinity()`, and since Rust 1.20 also `Float::nan()`. /// /// ``` /// use num_traits::Float; @@ -1214,7 +1214,7 @@ pub trait Float fn is_sign_positive(self) -> bool; /// Returns `true` if `self` is negative, including `-0.0`, - /// `Float::neg_infinity()`, and with newer versions of Rust `-f64::NAN`. + /// `Float::neg_infinity()`, and since Rust 1.20 also `-Float::nan()`. /// /// ``` /// use num_traits::Float;