Implement CoreFloat trait
This is a subset of the `Float` trait, but works with `no_std`. Some code was simplified by using `CoreFloat`.
This commit is contained in:
parent
afa81f80e4
commit
8a7f383eb1
|
@ -4,6 +4,7 @@ use core::num::Wrapping;
|
||||||
|
|
||||||
use identities::Zero;
|
use identities::Zero;
|
||||||
use bounds::Bounded;
|
use bounds::Bounded;
|
||||||
|
use float::CoreFloat;
|
||||||
|
|
||||||
/// A generic trait for converting a value to a number.
|
/// A generic trait for converting a value to a number.
|
||||||
pub trait ToPrimitive {
|
pub trait ToPrimitive {
|
||||||
|
@ -228,8 +229,7 @@ macro_rules! impl_to_primitive_float_to_float {
|
||||||
// NaN and +-inf are cast as they are.
|
// NaN and +-inf are cast as they are.
|
||||||
let n = $slf as f64;
|
let n = $slf as f64;
|
||||||
let max_value: $DstT = ::core::$DstT::MAX;
|
let max_value: $DstT = ::core::$DstT::MAX;
|
||||||
if n != n || n == f64::INFINITY || n == f64::NEG_INFINITY
|
if !CoreFloat::is_finite(n) || (-max_value as f64 <= n && n <= max_value as f64)
|
||||||
|| (-max_value as f64 <= n && n <= max_value as f64)
|
|
||||||
{
|
{
|
||||||
Some($slf as $DstT)
|
Some($slf as $DstT)
|
||||||
} else {
|
} else {
|
||||||
|
|
260
src/float.rs
260
src/float.rs
|
@ -1,16 +1,221 @@
|
||||||
#[cfg(feature = "std")]
|
use core::mem;
|
||||||
use std::mem;
|
use core::ops::Neg;
|
||||||
#[cfg(feature = "std")]
|
use core::num::FpCategory;
|
||||||
use std::ops::Neg;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::num::FpCategory;
|
|
||||||
|
|
||||||
// Used for default implementation of `epsilon`
|
// Used for default implementation of `epsilon`
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
|
use {Num, ToPrimitive};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use {Num, NumCast};
|
use NumCast;
|
||||||
|
|
||||||
|
/// Generic trait for floating point numbers that works with `no_std`.
|
||||||
|
///
|
||||||
|
/// This trait implements a subset of the `Float` trait.
|
||||||
|
pub trait CoreFloat: Num + Neg<Output = Self> + PartialOrd + Copy {
|
||||||
|
/// Returns positive infinity.
|
||||||
|
#[inline]
|
||||||
|
fn infinity() -> Self;
|
||||||
|
|
||||||
|
/// Returns negative infinity.
|
||||||
|
#[inline]
|
||||||
|
fn neg_infinity() -> Self;
|
||||||
|
|
||||||
|
/// Returns NaN.
|
||||||
|
#[inline]
|
||||||
|
fn nan() -> Self;
|
||||||
|
|
||||||
|
/// Returns `true` if the number is NaN.
|
||||||
|
#[inline]
|
||||||
|
fn is_nan(self) -> bool {
|
||||||
|
self != self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the number is infinite.
|
||||||
|
#[inline]
|
||||||
|
fn is_infinite(self) -> bool {
|
||||||
|
self == Self::infinity() || self == Self::neg_infinity()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the number is neither infinite or NaN.
|
||||||
|
#[inline]
|
||||||
|
fn is_finite(self) -> bool {
|
||||||
|
!(self.is_nan() || self.is_infinite())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN.
|
||||||
|
#[inline]
|
||||||
|
fn is_normal(self) -> bool {
|
||||||
|
self.classify() == FpCategory::Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[inline]
|
||||||
|
fn classify(self) -> FpCategory;
|
||||||
|
|
||||||
|
/// Computes the absolute value of `self`. Returns `CoreFloat::nan()` if the
|
||||||
|
/// number is `CoreFloat::nan()`.
|
||||||
|
#[inline]
|
||||||
|
fn abs(self) -> Self {
|
||||||
|
if self.is_sign_positive() {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if self.is_sign_negative() {
|
||||||
|
return -self;
|
||||||
|
}
|
||||||
|
Self::nan()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a number that represents the sign of `self`.
|
||||||
|
///
|
||||||
|
/// - `1.0` if the number is positive, `+0.0` or `CoreFloat::infinity()`
|
||||||
|
/// - `-1.0` if the number is negative, `-0.0` or `CoreFloat::neg_infinity()`
|
||||||
|
/// - `CoreFloat::nan()` if the number is `CoreFloat::nan()`
|
||||||
|
#[inline]
|
||||||
|
fn signum(self) -> Self {
|
||||||
|
if self.is_sign_positive() {
|
||||||
|
return Self::one();
|
||||||
|
}
|
||||||
|
if self.is_sign_negative() {
|
||||||
|
return -Self::one();
|
||||||
|
}
|
||||||
|
Self::nan()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` is positive, including `+0.0` and
|
||||||
|
/// `CoreFloat::infinity()`.
|
||||||
|
#[inline]
|
||||||
|
fn is_sign_positive(self) -> bool {
|
||||||
|
self > Self::zero() || (Self::one() / self) == Self::infinity()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if `self` is negative, including `-0.0` and
|
||||||
|
/// `CoreFloat::neg_infinity()`.
|
||||||
|
#[inline]
|
||||||
|
fn is_sign_negative(self) -> bool {
|
||||||
|
self < Self::zero() || (Self::one() / self) == Self::neg_infinity()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum of the two numbers.
|
||||||
|
///
|
||||||
|
/// If one of the arguments is NaN, then the other argument is returned.
|
||||||
|
#[inline]
|
||||||
|
fn min(self, other: Self) -> Self {
|
||||||
|
if self.is_nan() {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
if other.is_nan() {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if self < other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the maximum of the two numbers.
|
||||||
|
///
|
||||||
|
/// If one of the arguments is NaN, then the other argument is returned.
|
||||||
|
#[inline]
|
||||||
|
fn max(self, other: Self) -> Self {
|
||||||
|
if self.is_nan() {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
if other.is_nan() {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if self > other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the reciprocal (multiplicative inverse) of the number.
|
||||||
|
#[inline]
|
||||||
|
fn recip(self) -> Self {
|
||||||
|
Self::one() / self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raise a number to an integer power.
|
||||||
|
///
|
||||||
|
/// Using this function is generally faster than using `powf`
|
||||||
|
#[inline]
|
||||||
|
fn powi(mut self, mut exp: i32) -> Self {
|
||||||
|
if exp < 0 {
|
||||||
|
exp = -exp;
|
||||||
|
self = self.recip();
|
||||||
|
}
|
||||||
|
// It should always be possible to convert a positive `i32` to a `usize`.
|
||||||
|
super::pow(self, exp.to_usize().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to degrees, assuming the number is in radians.
|
||||||
|
#[inline]
|
||||||
|
fn to_degrees(self) -> Self;
|
||||||
|
|
||||||
|
/// Converts to radians, assuming the number is in degrees.
|
||||||
|
#[inline]
|
||||||
|
fn to_radians(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoreFloat for f32 {
|
||||||
|
fn infinity() -> Self {
|
||||||
|
::core::f32::INFINITY
|
||||||
|
}
|
||||||
|
fn neg_infinity() -> Self {
|
||||||
|
::core::f32::NEG_INFINITY
|
||||||
|
}
|
||||||
|
fn nan() -> Self {
|
||||||
|
::core::f32::NAN
|
||||||
|
}
|
||||||
|
fn classify(self) -> FpCategory {
|
||||||
|
const EXP_MASK: u32 = 0x7f800000;
|
||||||
|
const MAN_MASK: u32 = 0x007fffff;
|
||||||
|
|
||||||
|
let bits: u32 = unsafe { mem::transmute(self) };
|
||||||
|
match (bits & MAN_MASK, bits & EXP_MASK) {
|
||||||
|
(0, 0) => FpCategory::Zero,
|
||||||
|
(_, 0) => FpCategory::Subnormal,
|
||||||
|
(0, EXP_MASK) => FpCategory::Infinite,
|
||||||
|
(_, EXP_MASK) => FpCategory::Nan,
|
||||||
|
_ => FpCategory::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn to_degrees(self) -> Self {
|
||||||
|
self * (180.0 / ::core::f32::consts::PI)
|
||||||
|
}
|
||||||
|
fn to_radians(self) -> Self {
|
||||||
|
self * (::core::f32::consts::PI / 180.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoreFloat for f64 {
|
||||||
|
fn infinity() -> Self {
|
||||||
|
::core::f64::INFINITY
|
||||||
|
}
|
||||||
|
fn neg_infinity() -> Self {
|
||||||
|
::core::f64::NEG_INFINITY
|
||||||
|
}
|
||||||
|
fn nan() -> Self {
|
||||||
|
::core::f64::NAN
|
||||||
|
}
|
||||||
|
fn classify(self) -> FpCategory {
|
||||||
|
const EXP_MASK: u64 = 0x7ff0000000000000;
|
||||||
|
const MAN_MASK: u64 = 0x000fffffffffffff;
|
||||||
|
|
||||||
|
let bits: u64 = unsafe { mem::transmute(self) };
|
||||||
|
match (bits & MAN_MASK, bits & EXP_MASK) {
|
||||||
|
(0, 0) => FpCategory::Zero,
|
||||||
|
(_, 0) => FpCategory::Subnormal,
|
||||||
|
(0, EXP_MASK) => FpCategory::Infinite,
|
||||||
|
(_, EXP_MASK) => FpCategory::Nan,
|
||||||
|
_ => FpCategory::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn to_degrees(self) -> Self {
|
||||||
|
self * (180.0 / ::core::f64::consts::PI)
|
||||||
|
}
|
||||||
|
fn to_radians(self) -> Self {
|
||||||
|
self * (::core::f64::consts::PI / 180.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: these doctests aren't actually helpful, because they're using and
|
// FIXME: these doctests aren't actually helpful, because they're using and
|
||||||
// testing the inherent methods directly, not going through `Float`.
|
// testing the inherent methods directly, not going through `Float`.
|
||||||
|
@ -1328,25 +1533,40 @@ float_const_impl! {
|
||||||
SQRT_2,
|
SQRT_2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, feature = "std"))]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use Float;
|
use core::f64::consts;
|
||||||
|
|
||||||
|
const DEG_RAD_PAIRS: [(f64, f64); 7] = [
|
||||||
|
(0.0, 0.),
|
||||||
|
(22.5, consts::FRAC_PI_8),
|
||||||
|
(30.0, consts::FRAC_PI_6),
|
||||||
|
(45.0, consts::FRAC_PI_4),
|
||||||
|
(60.0, consts::FRAC_PI_3),
|
||||||
|
(90.0, consts::FRAC_PI_2),
|
||||||
|
(180.0, consts::PI),
|
||||||
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_deg_rad() {
|
fn convert_deg_rad() {
|
||||||
use core::f64::consts;
|
use CoreFloat;
|
||||||
|
|
||||||
const DEG_RAD_PAIRS: [(f64, f64); 7] = [
|
|
||||||
(0.0, 0.),
|
|
||||||
(22.5, consts::FRAC_PI_8),
|
|
||||||
(30.0, consts::FRAC_PI_6),
|
|
||||||
(45.0, consts::FRAC_PI_4),
|
|
||||||
(60.0, consts::FRAC_PI_3),
|
|
||||||
(90.0, consts::FRAC_PI_2),
|
|
||||||
(180.0, consts::PI),
|
|
||||||
];
|
|
||||||
|
|
||||||
for &(deg, rad) in &DEG_RAD_PAIRS {
|
for &(deg, rad) in &DEG_RAD_PAIRS {
|
||||||
|
assert!((CoreFloat::to_degrees(rad) - deg).abs() < 1e-6);
|
||||||
|
assert!((CoreFloat::to_radians(deg) - rad).abs() < 1e-6);
|
||||||
|
|
||||||
|
let (deg, rad) = (deg as f32, rad as f32);
|
||||||
|
assert!((CoreFloat::to_degrees(rad) - deg).abs() < 1e-6);
|
||||||
|
assert!((CoreFloat::to_radians(deg) - rad).abs() < 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[test]
|
||||||
|
fn convert_deg_rad_std() {
|
||||||
|
for &(deg, rad) in &DEG_RAD_PAIRS {
|
||||||
|
use Float;
|
||||||
|
|
||||||
assert!((Float::to_degrees(rad) - deg).abs() < 1e-6);
|
assert!((Float::to_degrees(rad) - deg).abs() < 1e-6);
|
||||||
assert!((Float::to_radians(deg) - rad).abs() < 1e-6);
|
assert!((Float::to_radians(deg) - rad).abs() < 1e-6);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ use core::fmt;
|
||||||
pub use bounds::Bounded;
|
pub use bounds::Bounded;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use float::Float;
|
pub use float::Float;
|
||||||
pub use float::FloatConst;
|
pub use float::{CoreFloat, FloatConst};
|
||||||
// pub use real::Real; // NOTE: Don't do this, it breaks `use num_traits::*;`.
|
// pub use real::Real; // NOTE: Don't do this, it breaks `use num_traits::*;`.
|
||||||
pub use identities::{Zero, One, zero, one};
|
pub use identities::{Zero, One, zero, one};
|
||||||
pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr};
|
pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr};
|
||||||
|
|
38
src/sign.rs
38
src/sign.rs
|
@ -3,6 +3,8 @@ use core::{f32, f64};
|
||||||
use core::num::Wrapping;
|
use core::num::Wrapping;
|
||||||
|
|
||||||
use Num;
|
use Num;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use float::CoreFloat;
|
||||||
|
|
||||||
/// Useful functions for signed numbers (i.e. numbers that can be negative).
|
/// Useful functions for signed numbers (i.e. numbers that can be negative).
|
||||||
pub trait Signed: Sized + Num + Neg<Output = Self> {
|
pub trait Signed: Sized + Num + Neg<Output = Self> {
|
||||||
|
@ -103,24 +105,10 @@ macro_rules! signed_float_impl {
|
||||||
impl Signed for $t {
|
impl Signed for $t {
|
||||||
/// Computes the absolute value. Returns `NAN` if the number is `NAN`.
|
/// Computes the absolute value. Returns `NAN` if the number is `NAN`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(feature = "std")]
|
|
||||||
fn abs(&self) -> $t {
|
fn abs(&self) -> $t {
|
||||||
(*self).abs()
|
(*self).abs()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the absolute value. Returns `NAN` if the number is `NAN`.
|
|
||||||
#[inline]
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
fn abs(&self) -> $t {
|
|
||||||
if self.is_positive() {
|
|
||||||
*self
|
|
||||||
} else if self.is_negative() {
|
|
||||||
-*self
|
|
||||||
} else {
|
|
||||||
$nan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The positive difference of two numbers. Returns `0.0` if the number is
|
/// The positive difference of two numbers. Returns `0.0` if the number is
|
||||||
/// less than or equal to `other`, otherwise the difference between`self`
|
/// less than or equal to `other`, otherwise the difference between`self`
|
||||||
/// and `other` is returned.
|
/// and `other` is returned.
|
||||||
|
@ -135,27 +123,9 @@ macro_rules! signed_float_impl {
|
||||||
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
||||||
/// - `NAN` if the number is NaN
|
/// - `NAN` if the number is NaN
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(feature = "std")]
|
|
||||||
fn signum(&self) -> $t {
|
fn signum(&self) -> $t {
|
||||||
use Float;
|
use CoreFloat;
|
||||||
Float::signum(*self)
|
CoreFloat::signum(*self)
|
||||||
}
|
|
||||||
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
|
|
||||||
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
|
||||||
/// - `NAN` if the number is NaN
|
|
||||||
#[inline]
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
fn signum(&self) -> $t {
|
|
||||||
if self.is_positive() {
|
|
||||||
1.0
|
|
||||||
} else if self.is_negative() {
|
|
||||||
-1.0
|
|
||||||
} else {
|
|
||||||
$nan
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
|
/// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
|
||||||
|
|
Loading…
Reference in New Issue