ordered-float-2.10.0/.cargo_vcs_info.json0000644000000001120000000000100136350ustar { "git": { "sha1": "f777611b74fd332d70448d831587b71eb0247eeb" } } ordered-float-2.10.0/.github/workflows/tests.yaml000064400000000000000000000023370072674642500201130ustar 00000000000000on: [push, pull_request] name: Tests jobs: test: name: Tests runs-on: ubuntu-latest strategy: matrix: rust: - stable - 1.36.0 steps: - name: Checkout uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: Test (default features) uses: actions-rs/cargo@v1 with: command: test - name: Test (no default features) uses: actions-rs/cargo@v1 with: command: test args: --no-default-features - name: Test (schema features subset) if: matrix.rust == 'stable' uses: actions-rs/cargo@v1 with: command: test args: --features "std,schemars" - name: Test (rand features subset) if: matrix.rust == 'stable' uses: actions-rs/cargo@v1 with: command: test args: --features "rand,randtest" - name: Test (all features) if: matrix.rust == 'stable' uses: actions-rs/cargo@v1 with: command: test args: --all-features ordered-float-2.10.0/.gitignore000064400000000000000000000002070072674642500144520ustar 00000000000000.DS_Store *~ *# *.o *.so *.swp *.dylib *.dSYM *.dll *.rlib *.dummy *.exe *-test /doc/ /target/ /examples/* !/examples/*.rs Cargo.lock ordered-float-2.10.0/.travis.yml000064400000000000000000000004170072674642500145760ustar 00000000000000language: rust rust: - 1.34.0 - nightly - beta - stable sudo: false env: matrix: - FEATURES="" - FEATURES="std" - FEATURES="serde" - FEATURES="std,serde" script: - cargo test -v --no-default-features --features "$FEATURES" ordered-float-2.10.0/Cargo.toml0000644000000027700000000000100116470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "ordered-float" version = "2.10.0" authors = ["Jonathan Reem ", "Matt Brubeck "] description = "Wrappers for total ordering on floats" readme = "README.md" keywords = ["no_std", "ord", "f64", "f32", "sort"] categories = ["science", "rust-patterns", "no-std"] license = "MIT" repository = "https://github.com/reem/rust-ordered-float" [dependencies.arbitrary] version = "1.0.0" optional = true [dependencies.num-traits] version = "0.2.1" default-features = false [dependencies.proptest] version = "1.0.0" optional = true [dependencies.rand] version = "0.8.3" optional = true default-features = false [dependencies.rkyv] version = "0.7" features = ["size_32"] optional = true default-features = false [dependencies.schemars] version = "0.6.5" optional = true [dependencies.serde] version = "1.0" optional = true default-features = false [dev-dependencies.serde_test] version = "1.0" [features] default = ["std"] randtest = ["rand/std", "rand/std_rng"] std = ["num-traits/std"] ordered-float-2.10.0/Cargo.toml.orig000064400000000000000000000020700072674642500153510ustar 00000000000000[package] name = "ordered-float" version = "2.10.0" authors = [ "Jonathan Reem ", "Matt Brubeck ", ] license = "MIT" description = "Wrappers for total ordering on floats" repository = "https://github.com/reem/rust-ordered-float" readme = "README.md" keywords = ["no_std", "ord", "f64", "f32", "sort"] categories = ["science", "rust-patterns", "no-std"] edition = "2018" [dependencies] num-traits = { version = "0.2.1", default-features = false } serde = { version = "1.0", optional = true, default-features = false } rkyv = { version = "0.7", optional = true, default-features = false, features = ["size_32"] } schemars = { version = "0.6.5", optional = true } rand = { version = "0.8.3", optional = true, default-features = false } arbitrary = { version = "1.0.0", optional = true } proptest = { version = "1.0.0", optional = true } [dev-dependencies] serde_test = "1.0" [features] default = ["std"] std = ["num-traits/std"] randtest = ["rand/std", "rand/std_rng"] ordered-float-2.10.0/LICENSE-MIT000064400000000000000000000020410072674642500141140ustar 00000000000000Copyright (c) 2015 Jonathan Reem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ordered-float-2.10.0/README.md000064400000000000000000000016410072674642500137440ustar 00000000000000# Ordered Floats Provides several wrapper types for Ord and Eq implementations on f64. ## Usage Use the crates.io repository; add this to your `Cargo.toml` along with the rest of your dependencies: ```toml [dependencies] ordered-float = "2.0" ``` See the [API documentation](https://docs.rs/ordered-float) for further details. ## no_std To use `ordered_float` without requiring the Rust standard library, disable the default `std` feature: ```toml [dependencies] ordered-float = { version = "2.0", default-features = false } ``` ## Optional features The following optional features can be enabled in `Cargo.toml`: * `rand`: Adds implementations for various distribution types provided by the `rand` crate. * `serde`: Implements the `serde::Serialize` and `serde::Deserialize` traits. * `schemars`: Implements the `schemars::JsonSchema` trait. * `proptest`: Implements the `proptest::Arbitrary` trait. ## License MIT ordered-float-2.10.0/rustfmt.toml000064400000000000000000000001640072674642500150650ustar 00000000000000# These two unstable options might improve the layout of the code: #fn_single_line = true #where_single_line = true ordered-float-2.10.0/src/lib.rs000064400000000000000000001622400072674642500143730ustar 00000000000000#![no_std] #![cfg_attr(test, deny(warnings))] #![deny(missing_docs)] //! Wrappers for total order on Floats. See the [`OrderedFloat`] and [`NotNan`] docs for details. #[cfg(feature = "std")] extern crate std; #[cfg(feature = "std")] use std::error::Error; use core::borrow::Borrow; use core::cmp::Ordering; use core::convert::TryFrom; use core::fmt; use core::hash::{Hash, Hasher}; use core::hint::unreachable_unchecked; use core::iter::{Product, Sum}; use core::num::FpCategory; use core::ops::{ Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; use core::str::FromStr; #[cfg(not(feature = "std"))] use num_traits::float::FloatCore as Float; #[cfg(feature = "std")] pub use num_traits::Float; use num_traits::{Bounded, FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero}; // masks for the parts of the IEEE 754 float const SIGN_MASK: u64 = 0x8000000000000000u64; const EXP_MASK: u64 = 0x7ff0000000000000u64; const MAN_MASK: u64 = 0x000fffffffffffffu64; // canonical raw bit patterns (for hashing) const CANONICAL_NAN_BITS: u64 = 0x7ff8000000000000u64; const CANONICAL_ZERO_BITS: u64 = 0x0u64; /// A wrapper around floats providing implementations of `Eq`, `Ord`, and `Hash`. /// /// NaN is sorted as *greater* than all other values and *equal* /// to itself, in contradiction with the IEEE standard. /// /// ``` /// use ordered_float::OrderedFloat; /// use std::f32::NAN; /// /// let mut v = [OrderedFloat(NAN), OrderedFloat(2.0), OrderedFloat(1.0)]; /// v.sort(); /// assert_eq!(v, [OrderedFloat(1.0), OrderedFloat(2.0), OrderedFloat(NAN)]); /// ``` /// /// Because `OrderedFloat` implements `Ord` and `Eq`, it can be used as a key in a `HashSet`, /// `HashMap`, `BTreeMap`, or `BTreeSet` (unlike the primitive `f32` or `f64` types): /// /// ``` /// # use ordered_float::OrderedFloat; /// # use std::collections::HashSet; /// # use std::f32::NAN; /// /// let mut s: HashSet> = HashSet::new(); /// s.insert(OrderedFloat(NAN)); /// assert!(s.contains(&OrderedFloat(NAN))); /// ``` #[derive(Debug, Default, Clone, Copy)] #[repr(transparent)] pub struct OrderedFloat(pub T); impl OrderedFloat { /// Get the value out. #[inline] pub fn into_inner(self) -> T { self.0 } } impl AsRef for OrderedFloat { #[inline] fn as_ref(&self) -> &T { &self.0 } } impl AsMut for OrderedFloat { #[inline] fn as_mut(&mut self) -> &mut T { &mut self.0 } } impl<'a, T: Float> From<&'a T> for &'a OrderedFloat { #[inline] fn from(t: &'a T) -> &'a OrderedFloat { // Safety: OrderedFloat is #[repr(transparent)] and has no invalid values. unsafe { &*(t as *const T as *const OrderedFloat) } } } impl<'a, T: Float> From<&'a mut T> for &'a mut OrderedFloat { #[inline] fn from(t: &'a mut T) -> &'a mut OrderedFloat { // Safety: OrderedFloat is #[repr(transparent)] and has no invalid values. unsafe { &mut *(t as *mut T as *mut OrderedFloat) } } } impl PartialOrd for OrderedFloat { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for OrderedFloat { fn cmp(&self, other: &Self) -> Ordering { let lhs = &self.0; let rhs = &other.0; match lhs.partial_cmp(rhs) { Some(ordering) => ordering, None => { if lhs.is_nan() { if rhs.is_nan() { Ordering::Equal } else { Ordering::Greater } } else { Ordering::Less } } } } } impl PartialEq for OrderedFloat { #[inline] fn eq(&self, other: &OrderedFloat) -> bool { if self.0.is_nan() { other.0.is_nan() } else { self.0 == other.0 } } } impl PartialEq for OrderedFloat { #[inline] fn eq(&self, other: &T) -> bool { self.0 == *other } } impl Hash for OrderedFloat { fn hash(&self, state: &mut H) { if self.is_nan() { // normalize to one representation of NaN hash_float(&T::nan(), state) } else { hash_float(&self.0, state) } } } impl fmt::Display for OrderedFloat { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } impl From> for f32 { #[inline] fn from(f: OrderedFloat) -> f32 { f.0 } } impl From> for f64 { #[inline] fn from(f: OrderedFloat) -> f64 { f.0 } } impl From for OrderedFloat { #[inline] fn from(val: T) -> Self { OrderedFloat(val) } } impl Deref for OrderedFloat { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for OrderedFloat { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Eq for OrderedFloat {} macro_rules! impl_ordered_float_binop { ($imp:ident, $method:ident, $assign_imp:ident, $assign_method:ident) => { impl $imp for OrderedFloat { type Output = OrderedFloat; #[inline] fn $method(self, other: Self) -> Self::Output { OrderedFloat((self.0).$method(other.0)) } } impl $imp for OrderedFloat { type Output = OrderedFloat; #[inline] fn $method(self, other: T) -> Self::Output { OrderedFloat((self.0).$method(other)) } } impl<'a, T> $imp<&'a T> for OrderedFloat where T: $imp<&'a T>, { type Output = OrderedFloat<>::Output>; #[inline] fn $method(self, other: &'a T) -> Self::Output { OrderedFloat((self.0).$method(other)) } } impl<'a, T> $imp<&'a Self> for OrderedFloat where T: $imp<&'a T>, { type Output = OrderedFloat<>::Output>; #[inline] fn $method(self, other: &'a Self) -> Self::Output { OrderedFloat((self.0).$method(&other.0)) } } impl<'a, T> $imp for &'a OrderedFloat where &'a T: $imp, { type Output = OrderedFloat<<&'a T as $imp>::Output>; #[inline] fn $method(self, other: Self) -> Self::Output { OrderedFloat((self.0).$method(&other.0)) } } impl<'a, T> $imp> for &'a OrderedFloat where &'a T: $imp, { type Output = OrderedFloat<<&'a T as $imp>::Output>; #[inline] fn $method(self, other: OrderedFloat) -> Self::Output { OrderedFloat((self.0).$method(other.0)) } } impl<'a, T> $imp for &'a OrderedFloat where &'a T: $imp, { type Output = OrderedFloat<<&'a T as $imp>::Output>; #[inline] fn $method(self, other: T) -> Self::Output { OrderedFloat((self.0).$method(other)) } } impl<'a, T> $imp<&'a T> for &'a OrderedFloat where &'a T: $imp, { type Output = OrderedFloat<<&'a T as $imp>::Output>; #[inline] fn $method(self, other: &'a T) -> Self::Output { OrderedFloat((self.0).$method(other)) } } #[doc(hidden)] // Added accidentally; remove in next major version impl<'a, T> $imp<&'a Self> for &'a OrderedFloat where &'a T: $imp, { type Output = OrderedFloat<<&'a T as $imp>::Output>; #[inline] fn $method(self, other: &'a Self) -> Self::Output { OrderedFloat((self.0).$method(&other.0)) } } impl $assign_imp for OrderedFloat { #[inline] fn $assign_method(&mut self, other: T) { (self.0).$assign_method(other); } } impl<'a, T: $assign_imp<&'a T>> $assign_imp<&'a T> for OrderedFloat { #[inline] fn $assign_method(&mut self, other: &'a T) { (self.0).$assign_method(other); } } impl $assign_imp for OrderedFloat { #[inline] fn $assign_method(&mut self, other: Self) { (self.0).$assign_method(other.0); } } impl<'a, T: $assign_imp<&'a T>> $assign_imp<&'a Self> for OrderedFloat { #[inline] fn $assign_method(&mut self, other: &'a Self) { (self.0).$assign_method(&other.0); } } }; } impl_ordered_float_binop! {Add, add, AddAssign, add_assign} impl_ordered_float_binop! {Sub, sub, SubAssign, sub_assign} impl_ordered_float_binop! {Mul, mul, MulAssign, mul_assign} impl_ordered_float_binop! {Div, div, DivAssign, div_assign} impl_ordered_float_binop! {Rem, rem, RemAssign, rem_assign} /// Adds a float directly. impl Sum for OrderedFloat { fn sum>>(iter: I) -> Self { OrderedFloat(iter.map(|v| v.0).sum()) } } impl<'a, T: Float + Sum + 'a> Sum<&'a OrderedFloat> for OrderedFloat { #[inline] fn sum>>(iter: I) -> Self { iter.cloned().sum() } } impl Product for OrderedFloat { fn product>>(iter: I) -> Self { OrderedFloat(iter.map(|v| v.0).product()) } } impl<'a, T: Float + Product + 'a> Product<&'a OrderedFloat> for OrderedFloat { #[inline] fn product>>(iter: I) -> Self { iter.cloned().product() } } impl Signed for OrderedFloat { #[inline] fn abs(&self) -> Self { OrderedFloat(self.0.abs()) } fn abs_sub(&self, other: &Self) -> Self { OrderedFloat(Signed::abs_sub(&self.0, &other.0)) } #[inline] fn signum(&self) -> Self { OrderedFloat(self.0.signum()) } #[inline] fn is_positive(&self) -> bool { self.0.is_positive() } #[inline] fn is_negative(&self) -> bool { self.0.is_negative() } } impl Bounded for OrderedFloat { #[inline] fn min_value() -> Self { OrderedFloat(T::min_value()) } #[inline] fn max_value() -> Self { OrderedFloat(T::max_value()) } } impl FromStr for OrderedFloat { type Err = T::Err; /// Convert a &str to `OrderedFloat`. Returns an error if the string fails to parse. /// /// ``` /// use ordered_float::OrderedFloat; /// /// assert!("-10".parse::>().is_ok()); /// assert!("abc".parse::>().is_err()); /// assert!("NaN".parse::>().is_ok()); /// ``` fn from_str(s: &str) -> Result { T::from_str(s).map(OrderedFloat) } } impl Neg for OrderedFloat { type Output = OrderedFloat; #[inline] fn neg(self) -> Self::Output { OrderedFloat(-self.0) } } impl<'a, T> Neg for &'a OrderedFloat where &'a T: Neg, { type Output = OrderedFloat<<&'a T as Neg>::Output>; #[inline] fn neg(self) -> Self::Output { OrderedFloat(-(&self.0)) } } impl Zero for OrderedFloat { #[inline] fn zero() -> Self { OrderedFloat(T::zero()) } #[inline] fn is_zero(&self) -> bool { self.0.is_zero() } } impl One for OrderedFloat { #[inline] fn one() -> Self { OrderedFloat(T::one()) } } impl NumCast for OrderedFloat { #[inline] fn from(n: F) -> Option { T::from(n).map(OrderedFloat) } } impl FromPrimitive for OrderedFloat { fn from_i64(n: i64) -> Option { T::from_i64(n).map(OrderedFloat) } fn from_u64(n: u64) -> Option { T::from_u64(n).map(OrderedFloat) } fn from_isize(n: isize) -> Option { T::from_isize(n).map(OrderedFloat) } fn from_i8(n: i8) -> Option { T::from_i8(n).map(OrderedFloat) } fn from_i16(n: i16) -> Option { T::from_i16(n).map(OrderedFloat) } fn from_i32(n: i32) -> Option { T::from_i32(n).map(OrderedFloat) } fn from_usize(n: usize) -> Option { T::from_usize(n).map(OrderedFloat) } fn from_u8(n: u8) -> Option { T::from_u8(n).map(OrderedFloat) } fn from_u16(n: u16) -> Option { T::from_u16(n).map(OrderedFloat) } fn from_u32(n: u32) -> Option { T::from_u32(n).map(OrderedFloat) } fn from_f32(n: f32) -> Option { T::from_f32(n).map(OrderedFloat) } fn from_f64(n: f64) -> Option { T::from_f64(n).map(OrderedFloat) } } impl ToPrimitive for OrderedFloat { fn to_i64(&self) -> Option { self.0.to_i64() } fn to_u64(&self) -> Option { self.0.to_u64() } fn to_isize(&self) -> Option { self.0.to_isize() } fn to_i8(&self) -> Option { self.0.to_i8() } fn to_i16(&self) -> Option { self.0.to_i16() } fn to_i32(&self) -> Option { self.0.to_i32() } fn to_usize(&self) -> Option { self.0.to_usize() } fn to_u8(&self) -> Option { self.0.to_u8() } fn to_u16(&self) -> Option { self.0.to_u16() } fn to_u32(&self) -> Option { self.0.to_u32() } fn to_f32(&self) -> Option { self.0.to_f32() } fn to_f64(&self) -> Option { self.0.to_f64() } } impl num_traits::float::FloatCore for OrderedFloat { fn nan() -> Self { OrderedFloat(T::nan()) } fn infinity() -> Self { OrderedFloat(T::infinity()) } fn neg_infinity() -> Self { OrderedFloat(T::neg_infinity()) } fn neg_zero() -> Self { OrderedFloat(T::neg_zero()) } fn min_value() -> Self { OrderedFloat(T::min_value()) } fn min_positive_value() -> Self { OrderedFloat(T::min_positive_value()) } fn max_value() -> Self { OrderedFloat(T::max_value()) } fn is_nan(self) -> bool { self.0.is_nan() } fn is_infinite(self) -> bool { self.0.is_infinite() } fn is_finite(self) -> bool { self.0.is_finite() } fn is_normal(self) -> bool { self.0.is_normal() } fn classify(self) -> FpCategory { self.0.classify() } fn floor(self) -> Self { OrderedFloat(self.0.floor()) } fn ceil(self) -> Self { OrderedFloat(self.0.ceil()) } fn round(self) -> Self { OrderedFloat(self.0.round()) } fn trunc(self) -> Self { OrderedFloat(self.0.trunc()) } fn fract(self) -> Self { OrderedFloat(self.0.fract()) } fn abs(self) -> Self { OrderedFloat(self.0.abs()) } fn signum(self) -> Self { OrderedFloat(self.0.signum()) } fn is_sign_positive(self) -> bool { self.0.is_sign_positive() } fn is_sign_negative(self) -> bool { self.0.is_sign_negative() } fn recip(self) -> Self { OrderedFloat(self.0.recip()) } fn powi(self, n: i32) -> Self { OrderedFloat(self.0.powi(n)) } fn integer_decode(self) -> (u64, i16, i8) { self.0.integer_decode() } fn epsilon() -> Self { OrderedFloat(T::epsilon()) } fn to_degrees(self) -> Self { OrderedFloat(self.0.to_degrees()) } fn to_radians(self) -> Self { OrderedFloat(self.0.to_radians()) } } #[cfg(feature = "std")] impl Float for OrderedFloat { fn nan() -> Self { OrderedFloat(T::nan()) } fn infinity() -> Self { OrderedFloat(T::infinity()) } fn neg_infinity() -> Self { OrderedFloat(T::neg_infinity()) } fn neg_zero() -> Self { OrderedFloat(T::neg_zero()) } fn min_value() -> Self { OrderedFloat(T::min_value()) } fn min_positive_value() -> Self { OrderedFloat(T::min_positive_value()) } fn max_value() -> Self { OrderedFloat(T::max_value()) } fn is_nan(self) -> bool { self.0.is_nan() } fn is_infinite(self) -> bool { self.0.is_infinite() } fn is_finite(self) -> bool { self.0.is_finite() } fn is_normal(self) -> bool { self.0.is_normal() } fn classify(self) -> FpCategory { self.0.classify() } fn floor(self) -> Self { OrderedFloat(self.0.floor()) } fn ceil(self) -> Self { OrderedFloat(self.0.ceil()) } fn round(self) -> Self { OrderedFloat(self.0.round()) } fn trunc(self) -> Self { OrderedFloat(self.0.trunc()) } fn fract(self) -> Self { OrderedFloat(self.0.fract()) } fn abs(self) -> Self { OrderedFloat(self.0.abs()) } fn signum(self) -> Self { OrderedFloat(self.0.signum()) } fn is_sign_positive(self) -> bool { self.0.is_sign_positive() } fn is_sign_negative(self) -> bool { self.0.is_sign_negative() } fn mul_add(self, a: Self, b: Self) -> Self { OrderedFloat(self.0.mul_add(a.0, b.0)) } fn recip(self) -> Self { OrderedFloat(self.0.recip()) } fn powi(self, n: i32) -> Self { OrderedFloat(self.0.powi(n)) } fn powf(self, n: Self) -> Self { OrderedFloat(self.0.powf(n.0)) } fn sqrt(self) -> Self { OrderedFloat(self.0.sqrt()) } fn exp(self) -> Self { OrderedFloat(self.0.exp()) } fn exp2(self) -> Self { OrderedFloat(self.0.exp2()) } fn ln(self) -> Self { OrderedFloat(self.0.ln()) } fn log(self, base: Self) -> Self { OrderedFloat(self.0.log(base.0)) } fn log2(self) -> Self { OrderedFloat(self.0.log2()) } fn log10(self) -> Self { OrderedFloat(self.0.log10()) } fn max(self, other: Self) -> Self { OrderedFloat(self.0.max(other.0)) } fn min(self, other: Self) -> Self { OrderedFloat(self.0.min(other.0)) } fn abs_sub(self, other: Self) -> Self { OrderedFloat(self.0.abs_sub(other.0)) } fn cbrt(self) -> Self { OrderedFloat(self.0.cbrt()) } fn hypot(self, other: Self) -> Self { OrderedFloat(self.0.hypot(other.0)) } fn sin(self) -> Self { OrderedFloat(self.0.sin()) } fn cos(self) -> Self { OrderedFloat(self.0.cos()) } fn tan(self) -> Self { OrderedFloat(self.0.tan()) } fn asin(self) -> Self { OrderedFloat(self.0.asin()) } fn acos(self) -> Self { OrderedFloat(self.0.acos()) } fn atan(self) -> Self { OrderedFloat(self.0.atan()) } fn atan2(self, other: Self) -> Self { OrderedFloat(self.0.atan2(other.0)) } fn sin_cos(self) -> (Self, Self) { let (a, b) = self.0.sin_cos(); (OrderedFloat(a), OrderedFloat(b)) } fn exp_m1(self) -> Self { OrderedFloat(self.0.exp_m1()) } fn ln_1p(self) -> Self { OrderedFloat(self.0.ln_1p()) } fn sinh(self) -> Self { OrderedFloat(self.0.sinh()) } fn cosh(self) -> Self { OrderedFloat(self.0.cosh()) } fn tanh(self) -> Self { OrderedFloat(self.0.tanh()) } fn asinh(self) -> Self { OrderedFloat(self.0.asinh()) } fn acosh(self) -> Self { OrderedFloat(self.0.acosh()) } fn atanh(self) -> Self { OrderedFloat(self.0.atanh()) } fn integer_decode(self) -> (u64, i16, i8) { self.0.integer_decode() } fn epsilon() -> Self { OrderedFloat(T::epsilon()) } fn to_degrees(self) -> Self { OrderedFloat(self.0.to_degrees()) } fn to_radians(self) -> Self { OrderedFloat(self.0.to_radians()) } } impl Num for OrderedFloat { type FromStrRadixErr = T::FromStrRadixErr; fn from_str_radix(str: &str, radix: u32) -> Result { T::from_str_radix(str, radix).map(OrderedFloat) } } /// A wrapper around floats providing an implementation of `Eq`, `Ord` and `Hash`. /// /// A NaN value cannot be stored in this type. /// /// ``` /// use ordered_float::NotNan; /// /// let mut v = [ /// NotNan::new(2.0).unwrap(), /// NotNan::new(1.0).unwrap(), /// ]; /// v.sort(); /// assert_eq!(v, [1.0, 2.0]); /// ``` /// /// Because `NotNan` implements `Ord` and `Eq`, it can be used as a key in a `HashSet`, /// `HashMap`, `BTreeMap`, or `BTreeSet` (unlike the primitive `f32` or `f64` types): /// /// ``` /// # use ordered_float::NotNan; /// # use std::collections::HashSet; /// /// let mut s: HashSet> = HashSet::new(); /// let key = NotNan::new(1.0).unwrap(); /// s.insert(key); /// assert!(s.contains(&key)); /// ``` /// /// Arithmetic on NotNan values will panic if it produces a NaN value: /// /// ```should_panic /// # use ordered_float::NotNan; /// let a = NotNan::new(std::f32::INFINITY).unwrap(); /// let b = NotNan::new(std::f32::NEG_INFINITY).unwrap(); /// /// // This will panic: /// let c = a + b; /// ``` #[derive(PartialOrd, PartialEq, Debug, Default, Clone, Copy)] #[repr(transparent)] pub struct NotNan(T); impl NotNan { /// Create a `NotNan` value. /// /// Returns `Err` if `val` is NaN pub fn new(val: T) -> Result { match val { ref val if val.is_nan() => Err(FloatIsNan), val => Ok(NotNan(val)), } } } impl NotNan { /// Get the value out. #[inline] pub fn into_inner(self) -> T { self.0 } /// Create a `NotNan` value from a value that is guaranteed to not be NaN /// /// # Safety /// /// Behaviour is undefined if `val` is NaN #[inline] pub const unsafe fn new_unchecked(val: T) -> Self { NotNan(val) } /// Create a `NotNan` value from a value that is guaranteed to not be NaN /// /// # Safety /// /// Behaviour is undefined if `val` is NaN #[deprecated( since = "2.5.0", note = "Please use the new_unchecked function instead." )] #[inline] pub const unsafe fn unchecked_new(val: T) -> Self { Self::new_unchecked(val) } } impl AsRef for NotNan { #[inline] fn as_ref(&self) -> &T { &self.0 } } impl Borrow for NotNan { #[inline] fn borrow(&self) -> &f32 { &self.0 } } impl Borrow for NotNan { #[inline] fn borrow(&self) -> &f64 { &self.0 } } #[allow(clippy::derive_ord_xor_partial_ord)] impl Ord for NotNan { fn cmp(&self, other: &NotNan) -> Ordering { match self.partial_cmp(&other) { Some(ord) => ord, None => unsafe { unreachable_unchecked() }, } } } #[allow(clippy::derive_hash_xor_eq)] impl Hash for NotNan { #[inline] fn hash(&self, state: &mut H) { hash_float(&self.0, state) } } impl fmt::Display for NotNan { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } } impl From> for f32 { #[inline] fn from(value: NotNan) -> Self { value.0 } } impl From> for f64 { #[inline] fn from(value: NotNan) -> Self { value.0 } } impl TryFrom for NotNan { type Error = FloatIsNan; #[inline] fn try_from(v: f32) -> Result { NotNan::new(v) } } impl TryFrom for NotNan { type Error = FloatIsNan; #[inline] fn try_from(v: f64) -> Result { NotNan::new(v) } } macro_rules! impl_from_int_primitive { ($primitive:ty, $inner:ty) => { impl From<$primitive> for NotNan<$inner> { fn from(source: $primitive) -> Self { // the primitives with which this macro will be called cannot hold a value that // f64::from would convert to NaN, so this does not hurt invariants NotNan(<$inner as From<$primitive>>::from(source)) } } }; } impl_from_int_primitive!(i8, f64); impl_from_int_primitive!(i16, f64); impl_from_int_primitive!(i32, f64); impl_from_int_primitive!(u8, f64); impl_from_int_primitive!(u16, f64); impl_from_int_primitive!(u32, f64); impl_from_int_primitive!(i8, f32); impl_from_int_primitive!(i16, f32); impl_from_int_primitive!(u8, f32); impl_from_int_primitive!(u16, f32); impl From> for NotNan { #[inline] fn from(v: NotNan) -> NotNan { unsafe { NotNan::new_unchecked(v.0 as f64) } } } impl Deref for NotNan { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl Eq for NotNan {} impl PartialEq for NotNan { #[inline] fn eq(&self, other: &T) -> bool { self.0 == *other } } /// Adds a float directly. /// /// Panics if the provided value is NaN or the computation results in NaN impl Add for NotNan { type Output = Self; #[inline] fn add(self, other: T) -> Self { NotNan::new(self.0 + other).expect("Addition resulted in NaN") } } /// Adds a float directly. /// /// Panics if the provided value is NaN. impl Sum for NotNan { fn sum>>(iter: I) -> Self { NotNan::new(iter.map(|v| v.0).sum()).expect("Sum resulted in NaN") } } impl<'a, T: Float + Sum + 'a> Sum<&'a NotNan> for NotNan { #[inline] fn sum>>(iter: I) -> Self { iter.cloned().sum() } } /// Subtracts a float directly. /// /// Panics if the provided value is NaN or the computation results in NaN impl Sub for NotNan { type Output = Self; #[inline] fn sub(self, other: T) -> Self { NotNan::new(self.0 - other).expect("Subtraction resulted in NaN") } } /// Multiplies a float directly. /// /// Panics if the provided value is NaN or the computation results in NaN impl Mul for NotNan { type Output = Self; #[inline] fn mul(self, other: T) -> Self { NotNan::new(self.0 * other).expect("Multiplication resulted in NaN") } } impl Product for NotNan { fn product>>(iter: I) -> Self { NotNan::new(iter.map(|v| v.0).product()).expect("Product resulted in NaN") } } impl<'a, T: Float + Product + 'a> Product<&'a NotNan> for NotNan { #[inline] fn product>>(iter: I) -> Self { iter.cloned().product() } } /// Divides a float directly. /// /// Panics if the provided value is NaN or the computation results in NaN impl Div for NotNan { type Output = Self; #[inline] fn div(self, other: T) -> Self { NotNan::new(self.0 / other).expect("Division resulted in NaN") } } /// Calculates `%` with a float directly. /// /// Panics if the provided value is NaN or the computation results in NaN impl Rem for NotNan { type Output = Self; #[inline] fn rem(self, other: T) -> Self { NotNan::new(self.0 % other).expect("Rem resulted in NaN") } } macro_rules! impl_not_nan_binop { ($imp:ident, $method:ident, $assign_imp:ident, $assign_method:ident) => { impl $imp for NotNan { type Output = Self; #[inline] fn $method(self, other: Self) -> Self { self.$method(other.0) } } impl $imp<&T> for NotNan { type Output = NotNan; #[inline] fn $method(self, other: &T) -> Self::Output { self.$method(*other) } } impl $imp<&Self> for NotNan { type Output = NotNan; #[inline] fn $method(self, other: &Self) -> Self::Output { self.$method(other.0) } } impl $imp for &NotNan { type Output = NotNan; #[inline] fn $method(self, other: Self) -> Self::Output { (*self).$method(other.0) } } impl $imp> for &NotNan { type Output = NotNan; #[inline] fn $method(self, other: NotNan) -> Self::Output { (*self).$method(other.0) } } impl $imp for &NotNan { type Output = NotNan; #[inline] fn $method(self, other: T) -> Self::Output { (*self).$method(other) } } impl $imp<&T> for &NotNan { type Output = NotNan; #[inline] fn $method(self, other: &T) -> Self::Output { (*self).$method(*other) } } impl $assign_imp for NotNan { #[inline] fn $assign_method(&mut self, other: T) { *self = (*self).$method(other); } } impl $assign_imp<&T> for NotNan { #[inline] fn $assign_method(&mut self, other: &T) { *self = (*self).$method(*other); } } impl $assign_imp for NotNan { #[inline] fn $assign_method(&mut self, other: Self) { (*self).$assign_method(other.0); } } impl $assign_imp<&Self> for NotNan { #[inline] fn $assign_method(&mut self, other: &Self) { (*self).$assign_method(other.0); } } }; } impl_not_nan_binop! {Add, add, AddAssign, add_assign} impl_not_nan_binop! {Sub, sub, SubAssign, sub_assign} impl_not_nan_binop! {Mul, mul, MulAssign, mul_assign} impl_not_nan_binop! {Div, div, DivAssign, div_assign} impl_not_nan_binop! {Rem, rem, RemAssign, rem_assign} impl Neg for NotNan { type Output = Self; #[inline] fn neg(self) -> Self { NotNan(-self.0) } } impl Neg for &NotNan { type Output = NotNan; #[inline] fn neg(self) -> Self::Output { NotNan(-self.0) } } /// An error indicating an attempt to construct NotNan from a NaN #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct FloatIsNan; #[cfg(feature = "std")] impl Error for FloatIsNan { fn description(&self) -> &str { "NotNan constructed with NaN" } } impl fmt::Display for FloatIsNan { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "NotNan constructed with NaN") } } #[cfg(feature = "std")] impl From for std::io::Error { #[inline] fn from(e: FloatIsNan) -> std::io::Error { std::io::Error::new(std::io::ErrorKind::InvalidInput, e) } } #[inline] fn hash_float(f: &F, state: &mut H) { raw_double_bits(f).hash(state); } #[inline] fn raw_double_bits(f: &F) -> u64 { if f.is_nan() { return CANONICAL_NAN_BITS; } let (man, exp, sign) = f.integer_decode(); if man == 0 { return CANONICAL_ZERO_BITS; } let exp_u64 = exp as u16 as u64; let sign_u64 = if sign > 0 { 1u64 } else { 0u64 }; (man & MAN_MASK) | ((exp_u64 << 52) & EXP_MASK) | ((sign_u64 << 63) & SIGN_MASK) } impl Zero for NotNan { #[inline] fn zero() -> Self { NotNan(T::zero()) } #[inline] fn is_zero(&self) -> bool { self.0.is_zero() } } impl One for NotNan { #[inline] fn one() -> Self { NotNan(T::one()) } } impl Bounded for NotNan { #[inline] fn min_value() -> Self { NotNan(T::min_value()) } #[inline] fn max_value() -> Self { NotNan(T::max_value()) } } impl FromStr for NotNan { type Err = ParseNotNanError; /// Convert a &str to `NotNan`. Returns an error if the string fails to parse, /// or if the resulting value is NaN /// /// ``` /// use ordered_float::NotNan; /// /// assert!("-10".parse::>().is_ok()); /// assert!("abc".parse::>().is_err()); /// assert!("NaN".parse::>().is_err()); /// ``` fn from_str(src: &str) -> Result { src.parse() .map_err(ParseNotNanError::ParseFloatError) .and_then(|f| NotNan::new(f).map_err(|_| ParseNotNanError::IsNaN)) } } impl FromPrimitive for NotNan { fn from_i64(n: i64) -> Option { T::from_i64(n).and_then(|n| NotNan::new(n).ok()) } fn from_u64(n: u64) -> Option { T::from_u64(n).and_then(|n| NotNan::new(n).ok()) } fn from_isize(n: isize) -> Option { T::from_isize(n).and_then(|n| NotNan::new(n).ok()) } fn from_i8(n: i8) -> Option { T::from_i8(n).and_then(|n| NotNan::new(n).ok()) } fn from_i16(n: i16) -> Option { T::from_i16(n).and_then(|n| NotNan::new(n).ok()) } fn from_i32(n: i32) -> Option { T::from_i32(n).and_then(|n| NotNan::new(n).ok()) } fn from_usize(n: usize) -> Option { T::from_usize(n).and_then(|n| NotNan::new(n).ok()) } fn from_u8(n: u8) -> Option { T::from_u8(n).and_then(|n| NotNan::new(n).ok()) } fn from_u16(n: u16) -> Option { T::from_u16(n).and_then(|n| NotNan::new(n).ok()) } fn from_u32(n: u32) -> Option { T::from_u32(n).and_then(|n| NotNan::new(n).ok()) } fn from_f32(n: f32) -> Option { T::from_f32(n).and_then(|n| NotNan::new(n).ok()) } fn from_f64(n: f64) -> Option { T::from_f64(n).and_then(|n| NotNan::new(n).ok()) } } impl ToPrimitive for NotNan { fn to_i64(&self) -> Option { self.0.to_i64() } fn to_u64(&self) -> Option { self.0.to_u64() } fn to_isize(&self) -> Option { self.0.to_isize() } fn to_i8(&self) -> Option { self.0.to_i8() } fn to_i16(&self) -> Option { self.0.to_i16() } fn to_i32(&self) -> Option { self.0.to_i32() } fn to_usize(&self) -> Option { self.0.to_usize() } fn to_u8(&self) -> Option { self.0.to_u8() } fn to_u16(&self) -> Option { self.0.to_u16() } fn to_u32(&self) -> Option { self.0.to_u32() } fn to_f32(&self) -> Option { self.0.to_f32() } fn to_f64(&self) -> Option { self.0.to_f64() } } /// An error indicating a parse error from a string for `NotNan`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ParseNotNanError { /// A plain parse error from the underlying float type. ParseFloatError(E), /// The parsed float value resulted in a NaN. IsNaN, } #[cfg(feature = "std")] impl Error for ParseNotNanError { fn description(&self) -> &str { "Error parsing a not-NaN floating point value" } fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ParseNotNanError::ParseFloatError(e) => Some(e), ParseNotNanError::IsNaN => None, } } } impl fmt::Display for ParseNotNanError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseNotNanError::ParseFloatError(e) => write!(f, "Parse error: {}", e), ParseNotNanError::IsNaN => write!(f, "NotNan parser encounter a NaN"), } } } impl Num for NotNan { type FromStrRadixErr = ParseNotNanError; fn from_str_radix(src: &str, radix: u32) -> Result { T::from_str_radix(src, radix) .map_err(ParseNotNanError::ParseFloatError) .and_then(|n| NotNan::new(n).map_err(|_| ParseNotNanError::IsNaN)) } } impl Signed for NotNan { #[inline] fn abs(&self) -> Self { NotNan(self.0.abs()) } fn abs_sub(&self, other: &Self) -> Self { NotNan::new(Signed::abs_sub(&self.0, &other.0)).expect("Subtraction resulted in NaN") } #[inline] fn signum(&self) -> Self { NotNan(self.0.signum()) } #[inline] fn is_positive(&self) -> bool { self.0.is_positive() } #[inline] fn is_negative(&self) -> bool { self.0.is_negative() } } impl NumCast for NotNan { fn from(n: F) -> Option { T::from(n).and_then(|n| NotNan::new(n).ok()) } } #[cfg(feature = "serde")] mod impl_serde { extern crate serde; use self::serde::de::{Error, Unexpected}; use self::serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::{NotNan, OrderedFloat}; use core::f64; #[cfg(not(feature = "std"))] use num_traits::float::FloatCore as Float; #[cfg(feature = "std")] use num_traits::Float; #[cfg(test)] extern crate serde_test; #[cfg(test)] use self::serde_test::{assert_de_tokens_error, assert_tokens, Token}; impl Serialize for OrderedFloat { #[inline] fn serialize(&self, s: S) -> Result { self.0.serialize(s) } } impl<'de, T: Float + Deserialize<'de>> Deserialize<'de> for OrderedFloat { #[inline] fn deserialize>(d: D) -> Result { T::deserialize(d).map(OrderedFloat) } } impl Serialize for NotNan { #[inline] fn serialize(&self, s: S) -> Result { self.0.serialize(s) } } impl<'de, T: Float + Deserialize<'de>> Deserialize<'de> for NotNan { fn deserialize>(d: D) -> Result { let float = T::deserialize(d)?; NotNan::new(float).map_err(|_| { Error::invalid_value(Unexpected::Float(f64::NAN), &"float (but not NaN)") }) } } #[test] fn test_ordered_float() { let float = OrderedFloat(1.0f64); assert_tokens(&float, &[Token::F64(1.0)]); } #[test] fn test_not_nan() { let float = NotNan(1.0f64); assert_tokens(&float, &[Token::F64(1.0)]); } #[test] fn test_fail_on_nan() { assert_de_tokens_error::>( &[Token::F64(f64::NAN)], "invalid value: floating point `NaN`, expected float (but not NaN)", ); } } #[cfg(feature = "rkyv")] mod impl_rkyv { use super::{NotNan, OrderedFloat}; #[cfg(not(feature = "std"))] use num_traits::float::FloatCore as Float; #[cfg(feature = "std")] use num_traits::Float; #[cfg(test)] use rkyv::{archived_root, ser::Serializer}; use rkyv::{from_archived, Archive, Deserialize, Fallible, Serialize}; #[cfg(test)] type DefaultSerializer = rkyv::ser::serializers::CoreSerializer<16, 16>; #[cfg(test)] type DefaultDeserializer = rkyv::Infallible; impl Archive for OrderedFloat { type Archived = OrderedFloat; type Resolver = (); unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { out.write(*self); } } impl, S: Fallible + ?Sized> Serialize for OrderedFloat { fn serialize(&self, _: &mut S) -> Result { Ok(()) } } impl, D: Fallible + ?Sized> Deserialize, D> for OrderedFloat { fn deserialize(&self, _: &mut D) -> Result, D::Error> { Ok(from_archived!(*self)) } } impl Archive for NotNan { type Archived = NotNan; type Resolver = (); unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { out.write(*self); } } impl, S: Fallible + ?Sized> Serialize for NotNan { fn serialize(&self, _: &mut S) -> Result { Ok(()) } } impl, D: Fallible + ?Sized> Deserialize, D> for NotNan { fn deserialize(&self, _: &mut D) -> Result, D::Error> { Ok(from_archived!(*self)) } } #[test] fn test_ordered_float() { let float = OrderedFloat(1.0f64); let mut serializer = DefaultSerializer::default(); serializer .serialize_value(&float) .expect("failed to archive value"); let len = serializer.pos(); let buffer = serializer.into_serializer().into_inner(); let archived_value = unsafe { archived_root::>(&buffer[0..len]) }; assert_eq!(archived_value, &float); let mut deserializer = DefaultDeserializer::default(); let deser_float: OrderedFloat = archived_value.deserialize(&mut deserializer).unwrap(); assert_eq!(deser_float, float); } #[test] fn test_not_nan() { let float = NotNan(1.0f64); let mut serializer = DefaultSerializer::default(); serializer .serialize_value(&float) .expect("failed to archive value"); let len = serializer.pos(); let buffer = serializer.into_serializer().into_inner(); let archived_value = unsafe { archived_root::>(&buffer[0..len]) }; assert_eq!(archived_value, &float); let mut deserializer = DefaultDeserializer::default(); let deser_float: NotNan = archived_value.deserialize(&mut deserializer).unwrap(); assert_eq!(deser_float, float); } } #[cfg(all(feature = "std", feature = "schemars"))] mod impl_schemars { extern crate schemars; use self::schemars::gen::SchemaGenerator; use self::schemars::schema::{InstanceType, Schema, SchemaObject}; use super::{NotNan, OrderedFloat}; macro_rules! primitive_float_impl { ($type:ty, $schema_name:literal) => { impl schemars::JsonSchema for $type { fn is_referenceable() -> bool { false } fn schema_name() -> std::string::String { std::string::String::from($schema_name) } fn json_schema(_: &mut SchemaGenerator) -> Schema { SchemaObject { instance_type: Some(InstanceType::Number.into()), format: Some(std::string::String::from($schema_name)), ..Default::default() } .into() } } }; } primitive_float_impl!(OrderedFloat, "float"); primitive_float_impl!(OrderedFloat, "double"); primitive_float_impl!(NotNan, "float"); primitive_float_impl!(NotNan, "double"); #[test] fn schema_generation_does_not_panic_for_common_floats() { { let schema = schemars::gen::SchemaGenerator::default() .into_root_schema_for::>(); assert_eq!( schema.schema.instance_type, Some(schemars::schema::SingleOrVec::Single(std::boxed::Box::new( schemars::schema::InstanceType::Number ))) ); assert_eq!( schema.schema.metadata.unwrap().title.unwrap(), std::string::String::from("float") ); } { let schema = schemars::gen::SchemaGenerator::default() .into_root_schema_for::>(); assert_eq!( schema.schema.instance_type, Some(schemars::schema::SingleOrVec::Single(std::boxed::Box::new( schemars::schema::InstanceType::Number ))) ); assert_eq!( schema.schema.metadata.unwrap().title.unwrap(), std::string::String::from("double") ); } { let schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::>(); assert_eq!( schema.schema.instance_type, Some(schemars::schema::SingleOrVec::Single(std::boxed::Box::new( schemars::schema::InstanceType::Number ))) ); assert_eq!( schema.schema.metadata.unwrap().title.unwrap(), std::string::String::from("float") ); } { let schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::>(); assert_eq!( schema.schema.instance_type, Some(schemars::schema::SingleOrVec::Single(std::boxed::Box::new( schemars::schema::InstanceType::Number ))) ); assert_eq!( schema.schema.metadata.unwrap().title.unwrap(), std::string::String::from("double") ); } } #[test] fn ordered_float_schema_match_primitive_schema() { { let of_schema = schemars::gen::SchemaGenerator::default() .into_root_schema_for::>(); let prim_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); assert_eq!(of_schema, prim_schema); } { let of_schema = schemars::gen::SchemaGenerator::default() .into_root_schema_for::>(); let prim_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); assert_eq!(of_schema, prim_schema); } { let of_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::>(); let prim_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); assert_eq!(of_schema, prim_schema); } { let of_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::>(); let prim_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); assert_eq!(of_schema, prim_schema); } } } #[cfg(feature = "rand")] mod impl_rand { use super::{NotNan, OrderedFloat}; use rand::distributions::uniform::*; use rand::distributions::{Distribution, Open01, OpenClosed01, Standard}; use rand::Rng; macro_rules! impl_distribution { ($dist:ident, $($f:ty),+) => { $( impl Distribution> for $dist { fn sample(&self, rng: &mut R) -> NotNan<$f> { // 'rand' never generates NaN values in the Standard, Open01, or // OpenClosed01 distributions. Using 'new_unchecked' is therefore // safe. unsafe { NotNan::new_unchecked(self.sample(rng)) } } } impl Distribution> for $dist { fn sample(&self, rng: &mut R) -> OrderedFloat<$f> { OrderedFloat(self.sample(rng)) } } )* } } impl_distribution! { Standard, f32, f64 } impl_distribution! { Open01, f32, f64 } impl_distribution! { OpenClosed01, f32, f64 } pub struct UniformNotNan(UniformFloat); impl SampleUniform for NotNan { type Sampler = UniformNotNan; } impl SampleUniform for NotNan { type Sampler = UniformNotNan; } pub struct UniformOrdered(UniformFloat); impl SampleUniform for OrderedFloat { type Sampler = UniformOrdered; } impl SampleUniform for OrderedFloat { type Sampler = UniformOrdered; } macro_rules! impl_uniform_sampler { ($f:ty) => { impl UniformSampler for UniformNotNan<$f> { type X = NotNan<$f>; fn new(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformNotNan(UniformFloat::<$f>::new(low.borrow().0, high.borrow().0)) } fn new_inclusive(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformSampler::new(low, high) } fn sample(&self, rng: &mut R) -> Self::X { // UniformFloat.sample() will never return NaN. unsafe { NotNan::new_unchecked(self.0.sample(rng)) } } } impl UniformSampler for UniformOrdered<$f> { type X = OrderedFloat<$f>; fn new(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformOrdered(UniformFloat::<$f>::new(low.borrow().0, high.borrow().0)) } fn new_inclusive(low: B1, high: B2) -> Self where B1: SampleBorrow + Sized, B2: SampleBorrow + Sized, { UniformSampler::new(low, high) } fn sample(&self, rng: &mut R) -> Self::X { OrderedFloat(self.0.sample(rng)) } } }; } impl_uniform_sampler! { f32 } impl_uniform_sampler! { f64 } #[cfg(all(test, feature = "randtest"))] mod tests { use super::*; fn sample_fuzz() where Standard: Distribution>, Open01: Distribution>, OpenClosed01: Distribution>, Standard: Distribution>, Open01: Distribution>, OpenClosed01: Distribution>, T: crate::Float, { let mut rng = rand::thread_rng(); let f1: NotNan = rng.sample(Standard); let f2: NotNan = rng.sample(Open01); let f3: NotNan = rng.sample(OpenClosed01); let _: OrderedFloat = rng.sample(Standard); let _: OrderedFloat = rng.sample(Open01); let _: OrderedFloat = rng.sample(OpenClosed01); assert!(!f1.into_inner().is_nan()); assert!(!f2.into_inner().is_nan()); assert!(!f3.into_inner().is_nan()); } #[test] fn sampling_f32_does_not_panic() { sample_fuzz::(); } #[test] fn sampling_f64_does_not_panic() { sample_fuzz::(); } #[test] #[should_panic] fn uniform_sampling_panic_on_infinity_notnan() { let (low, high) = ( NotNan::new(0f64).unwrap(), NotNan::new(core::f64::INFINITY).unwrap(), ); let uniform = Uniform::new(low, high); let _ = uniform.sample(&mut rand::thread_rng()); } #[test] #[should_panic] fn uniform_sampling_panic_on_infinity_ordered() { let (low, high) = (OrderedFloat(0f64), OrderedFloat(core::f64::INFINITY)); let uniform = Uniform::new(low, high); let _ = uniform.sample(&mut rand::thread_rng()); } #[test] #[should_panic] fn uniform_sampling_panic_on_nan_ordered() { let (low, high) = (OrderedFloat(0f64), OrderedFloat(core::f64::NAN)); let uniform = Uniform::new(low, high); let _ = uniform.sample(&mut rand::thread_rng()); } } } #[cfg(feature = "proptest")] mod impl_proptest { use super::{NotNan, OrderedFloat}; use proptest::arbitrary::{Arbitrary, StrategyFor}; use proptest::num::{f32, f64}; use proptest::strategy::{FilterMap, Map, Strategy}; use std::convert::TryFrom; macro_rules! impl_arbitrary { ($($f:ident),+) => { $( impl Arbitrary for NotNan<$f> { type Strategy = FilterMap, fn(_: $f) -> Option>>; type Parameters = <$f as Arbitrary>::Parameters; fn arbitrary_with(params: Self::Parameters) -> Self::Strategy { <$f>::arbitrary_with(params) .prop_filter_map("filter nan values", |f| NotNan::try_from(f).ok()) } } impl Arbitrary for OrderedFloat<$f> { type Strategy = Map, fn(_: $f) -> OrderedFloat<$f>>; type Parameters = <$f as Arbitrary>::Parameters; fn arbitrary_with(params: Self::Parameters) -> Self::Strategy { <$f>::arbitrary_with(params).prop_map(|f| OrderedFloat::from(f)) } } )* } } impl_arbitrary! { f32, f64 } } #[cfg(feature = "arbitrary")] mod impl_arbitrary { use super::{FloatIsNan, NotNan, OrderedFloat}; use arbitrary::{Arbitrary, Unstructured}; use num_traits::FromPrimitive; macro_rules! impl_arbitrary { ($($f:ident),+) => { $( impl<'a> Arbitrary<'a> for NotNan<$f> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let float: $f = u.arbitrary()?; match NotNan::new(float) { Ok(notnan_value) => Ok(notnan_value), Err(FloatIsNan) => { // If our arbitrary float input was a NaN (encoded by exponent = max // value), then replace it with a finite float, reusing the mantissa // bits. // // This means the output is not uniformly distributed among all // possible float values, but Arbitrary makes no promise that that // is true. // // An alternative implementation would be to return an // `arbitrary::Error`, but that is not as useful since it forces the // caller to retry with new random/fuzzed data; and the precendent of // `arbitrary`'s built-in implementations is to prefer the approach of // mangling the input bits to fit. let (mantissa, _exponent, sign) = num_traits::Float::integer_decode(float); let revised_float = <$f>::from_i64( i64::from(sign) * mantissa as i64 ).unwrap(); // If this unwrap() fails, then there is a bug in the above code. Ok(NotNan::new(revised_float).unwrap()) } } } fn size_hint(depth: usize) -> (usize, Option) { <$f as Arbitrary>::size_hint(depth) } } impl<'a> Arbitrary<'a> for OrderedFloat<$f> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let float: $f = u.arbitrary()?; Ok(OrderedFloat::from(float)) } fn size_hint(depth: usize) -> (usize, Option) { <$f as Arbitrary>::size_hint(depth) } } )* } } impl_arbitrary! { f32, f64 } } ordered-float-2.10.0/tests/test.rs000064400000000000000000000561270072674642500151650ustar 00000000000000#![allow(clippy::float_cmp, clippy::eq_op, clippy::op_ref)] extern crate num_traits; extern crate ordered_float; #[cfg(not(feature = "std"))] pub use num_traits::float::FloatCore as Float; #[cfg(feature = "std")] pub use num_traits::Float; pub use num_traits::{Bounded, FromPrimitive, Num, One, Signed, ToPrimitive, Zero}; pub use ordered_float::*; pub use std::cmp::Ordering::*; pub use std::convert::TryFrom; pub use std::{f32, f64, panic}; pub use std::collections::hash_map::RandomState; pub use std::collections::HashSet; pub use std::hash::*; fn not_nan(x: T) -> NotNan { NotNan::new(x).unwrap() } #[test] fn ordered_f32_compare_regular_floats() { assert_eq!(OrderedFloat(7.0f32).cmp(&OrderedFloat(7.0)), Equal); assert_eq!(OrderedFloat(8.0f32).cmp(&OrderedFloat(7.0)), Greater); assert_eq!(OrderedFloat(4.0f32).cmp(&OrderedFloat(7.0)), Less); } #[test] fn ordered_f32_compare_regular_floats_op() { assert!(OrderedFloat(7.0f32) == OrderedFloat(7.0)); assert!(OrderedFloat(7.0f32) <= OrderedFloat(7.0)); assert!(OrderedFloat(7.0f32) >= OrderedFloat(7.0)); assert!(OrderedFloat(8.0f32) > OrderedFloat(7.0)); assert!(OrderedFloat(8.0f32) >= OrderedFloat(7.0)); assert!(OrderedFloat(4.0f32) < OrderedFloat(7.0)); assert!(OrderedFloat(4.0f32) <= OrderedFloat(7.0)); } #[test] fn ordered_f32_compare_nan() { let f32_nan: f32 = Float::nan(); assert_eq!( OrderedFloat(f32_nan).cmp(&OrderedFloat(Float::nan())), Equal ); assert_eq!( OrderedFloat(f32_nan).cmp(&OrderedFloat(-100000.0f32)), Greater ); assert_eq!( OrderedFloat(-100.0f32).cmp(&OrderedFloat(Float::nan())), Less ); } #[test] fn ordered_f32_compare_nan_op() { let f32_nan: OrderedFloat = OrderedFloat(Float::nan()); assert!(f32_nan == f32_nan); assert!(f32_nan <= f32_nan); assert!(f32_nan >= f32_nan); assert!(f32_nan > OrderedFloat(-100000.0f32)); assert!(f32_nan >= OrderedFloat(-100000.0f32)); assert!(OrderedFloat(-100.0f32) < f32_nan); assert!(OrderedFloat(-100.0f32) <= f32_nan); assert!(f32_nan > OrderedFloat(Float::infinity())); assert!(f32_nan >= OrderedFloat(Float::infinity())); assert!(f32_nan > OrderedFloat(Float::neg_infinity())); assert!(f32_nan >= OrderedFloat(Float::neg_infinity())); } #[test] fn ordered_f64_compare_regular_floats() { assert_eq!(OrderedFloat(7.0f64).cmp(&OrderedFloat(7.0)), Equal); assert_eq!(OrderedFloat(8.0f64).cmp(&OrderedFloat(7.0)), Greater); assert_eq!(OrderedFloat(4.0f64).cmp(&OrderedFloat(7.0)), Less); } #[test] fn not_nan32_zero() { assert_eq!(NotNan::::zero(), 0.0f32); assert!(NotNan::::zero().is_zero()); } #[test] fn not_nan32_one() { assert_eq!(NotNan::::one(), 1.0f32) } #[test] fn not_nan32_bounded() { assert_eq!(NotNan::::min_value(), ::min_value()); assert_eq!(NotNan::::max_value(), ::max_value()); } #[test] fn not_nan32_from_primitive() { assert_eq!(NotNan::::from_i8(42i8), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u8(42u8), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i16(42i16), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u16(42u16), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i32(42i32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u32(42u32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i64(42i64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u64(42u64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_isize(42isize), Some(not_nan(42.0))); assert_eq!(NotNan::::from_usize(42usize), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f32(42f32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f32(42f32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f32(Float::nan()), None); assert_eq!(NotNan::::from_f64(Float::nan()), None); } #[test] fn not_nan32_to_primitive() { let x = not_nan(42.0f32); assert_eq!(x.to_u8(), Some(42u8)); assert_eq!(x.to_i8(), Some(42i8)); assert_eq!(x.to_u16(), Some(42u16)); assert_eq!(x.to_i16(), Some(42i16)); assert_eq!(x.to_u32(), Some(42u32)); assert_eq!(x.to_i32(), Some(42i32)); assert_eq!(x.to_u64(), Some(42u64)); assert_eq!(x.to_i64(), Some(42i64)); assert_eq!(x.to_usize(), Some(42usize)); assert_eq!(x.to_isize(), Some(42isize)); assert_eq!(x.to_f32(), Some(42f32)); assert_eq!(x.to_f32(), Some(42f32)); assert_eq!(x.to_f64(), Some(42f64)); assert_eq!(x.to_f64(), Some(42f64)); } #[test] fn not_nan32_num() { assert_eq!(NotNan::::from_str_radix("42.0", 10).unwrap(), 42.0f32); assert!(NotNan::::from_str_radix("NaN", 10).is_err()); } #[test] fn not_nan32_signed() { assert_eq!(not_nan(42f32).abs(), 42f32); assert_eq!(not_nan(-42f32).abs(), 42f32); assert_eq!(not_nan(50f32).abs_sub(¬_nan(8f32)), 42f32); assert_eq!(not_nan(8f32).abs_sub(¬_nan(50f32)), 0f32); } #[test] fn not_nan32_num_cast() { assert_eq!( as num_traits::NumCast>::from(42).unwrap(), 42f32 ); assert_eq!( as num_traits::NumCast>::from(f32::nan()), None); } #[test] fn ordered_f64_compare_nan() { let f64_nan: f64 = Float::nan(); assert_eq!( OrderedFloat(f64_nan).cmp(&OrderedFloat(Float::nan())), Equal ); assert_eq!( OrderedFloat(f64_nan).cmp(&OrderedFloat(-100000.0f64)), Greater ); assert_eq!( OrderedFloat(-100.0f64).cmp(&OrderedFloat(Float::nan())), Less ); } #[test] fn ordered_f64_compare_regular_floats_op() { assert!(OrderedFloat(7.0) == OrderedFloat(7.0)); assert!(OrderedFloat(7.0) <= OrderedFloat(7.0)); assert!(OrderedFloat(7.0) >= OrderedFloat(7.0)); assert!(OrderedFloat(8.0) > OrderedFloat(7.0)); assert!(OrderedFloat(8.0) >= OrderedFloat(7.0)); assert!(OrderedFloat(4.0) < OrderedFloat(7.0)); assert!(OrderedFloat(4.0) <= OrderedFloat(7.0)); } #[test] fn ordered_f64_compare_nan_op() { let f64_nan: OrderedFloat = OrderedFloat(Float::nan()); assert!(f64_nan == f64_nan); assert!(f64_nan <= f64_nan); assert!(f64_nan >= f64_nan); assert!(f64_nan > OrderedFloat(-100000.0)); assert!(f64_nan >= OrderedFloat(-100000.0)); assert!(OrderedFloat(-100.0) < f64_nan); assert!(OrderedFloat(-100.0) <= f64_nan); assert!(f64_nan > OrderedFloat(Float::infinity())); assert!(f64_nan >= OrderedFloat(Float::infinity())); assert!(f64_nan > OrderedFloat(Float::neg_infinity())); assert!(f64_nan >= OrderedFloat(Float::neg_infinity())); } #[test] fn not_nan32_compare_regular_floats() { assert_eq!(not_nan(7.0f32).cmp(¬_nan(7.0)), Equal); assert_eq!(not_nan(8.0f32).cmp(¬_nan(7.0)), Greater); assert_eq!(not_nan(4.0f32).cmp(¬_nan(7.0)), Less); } #[test] fn not_nan32_fail_when_constructing_with_nan() { let f32_nan: f32 = Float::nan(); assert!(NotNan::new(f32_nan).is_err()); } #[test] fn not_nan32_calculate_correctly() { assert_eq!(*(not_nan(5.0f32) + not_nan(4.0f32)), 5.0f32 + 4.0f32); assert_eq!(*(not_nan(5.0f32) + 4.0f32), 5.0f32 + 4.0f32); assert_eq!(*(not_nan(5.0f32) - not_nan(4.0f32)), 5.0f32 - 4.0f32); assert_eq!(*(not_nan(5.0f32) - 4.0f32), 5.0f32 - 4.0f32); assert_eq!(*(not_nan(5.0f32) * not_nan(4.0f32)), 5.0f32 * 4.0f32); assert_eq!(*(not_nan(5.0f32) * 4.0f32), 5.0f32 * 4.0f32); assert_eq!(*(not_nan(8.0f32) / not_nan(4.0f32)), 8.0f32 / 4.0f32); assert_eq!(*(not_nan(8.0f32) / 4.0f32), 8.0f32 / 4.0f32); assert_eq!(*(not_nan(8.0f32) % not_nan(4.0f32)), 8.0f32 % 4.0f32); assert_eq!(*(not_nan(8.0f32) % 4.0f32), 8.0f32 % 4.0f32); assert_eq!(*(-not_nan(1.0f32)), -1.0f32); assert!(panic::catch_unwind(|| not_nan(0.0f32) + f32::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f32) - f32::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f32) * f32::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f32) / f32::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f32) % f32::NAN).is_err()); let mut number = not_nan(5.0f32); number += not_nan(4.0f32); assert_eq!(*number, 9.0f32); number -= not_nan(4.0f32); assert_eq!(*number, 5.0f32); number *= not_nan(4.0f32); assert_eq!(*number, 20.0f32); number /= not_nan(4.0f32); assert_eq!(*number, 5.0f32); number %= not_nan(4.0f32); assert_eq!(*number, 1.0f32); number = not_nan(5.0f32); number += 4.0f32; assert_eq!(*number, 9.0f32); number -= 4.0f32; assert_eq!(*number, 5.0f32); number *= 4.0f32; assert_eq!(*number, 20.0f32); number /= 4.0f32; assert_eq!(*number, 5.0f32); number %= 4.0f32; assert_eq!(*number, 1.0f32); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f32); tmp += f32::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f32); tmp -= f32::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f32); tmp *= f32::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f32); tmp /= f32::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f32); tmp %= f32::NAN; }) .is_err()); } #[test] fn not_nan64_compare_regular_floats() { assert_eq!(not_nan(7.0f64).cmp(¬_nan(7.0)), Equal); assert_eq!(not_nan(8.0f64).cmp(¬_nan(7.0)), Greater); assert_eq!(not_nan(4.0f64).cmp(¬_nan(7.0)), Less); } #[test] fn not_nan64_fail_when_constructing_with_nan() { let f64_nan: f64 = Float::nan(); assert!(NotNan::new(f64_nan).is_err()); } #[test] fn not_nan64_calculate_correctly() { assert_eq!(*(not_nan(5.0f64) + not_nan(4.0f64)), 5.0f64 + 4.0f64); assert_eq!(*(not_nan(5.0f64) + 4.0f64), 5.0f64 + 4.0f64); assert_eq!(*(not_nan(5.0f64) - not_nan(4.0f64)), 5.0f64 - 4.0f64); assert_eq!(*(not_nan(5.0f64) - 4.0f64), 5.0f64 - 4.0f64); assert_eq!(*(not_nan(5.0f64) * not_nan(4.0f64)), 5.0f64 * 4.0f64); assert_eq!(*(not_nan(5.0f64) * 4.0f64), 5.0f64 * 4.0f64); assert_eq!(*(not_nan(8.0f64) / not_nan(4.0f64)), 8.0f64 / 4.0f64); assert_eq!(*(not_nan(8.0f64) / 4.0f64), 8.0f64 / 4.0f64); assert_eq!(*(not_nan(8.0f64) % not_nan(4.0f64)), 8.0f64 % 4.0f64); assert_eq!(*(not_nan(8.0f64) % 4.0f64), 8.0f64 % 4.0f64); assert_eq!(*(-not_nan(1.0f64)), -1.0f64); assert!(panic::catch_unwind(|| not_nan(0.0f64) + f64::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f64) - f64::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f64) * f64::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f64) / f64::NAN).is_err()); assert!(panic::catch_unwind(|| not_nan(0.0f64) % f64::NAN).is_err()); let mut number = not_nan(5.0f64); number += not_nan(4.0f64); assert_eq!(*number, 9.0f64); number -= not_nan(4.0f64); assert_eq!(*number, 5.0f64); number *= not_nan(4.0f64); assert_eq!(*number, 20.0f64); number /= not_nan(4.0f64); assert_eq!(*number, 5.0f64); number %= not_nan(4.0f64); assert_eq!(*number, 1.0f64); number = not_nan(5.0f64); number += 4.0f64; assert_eq!(*number, 9.0f64); number -= 4.0f64; assert_eq!(*number, 5.0f64); number *= 4.0f64; assert_eq!(*number, 20.0f64); number /= 4.0f64; assert_eq!(*number, 5.0f64); number %= 4.0f64; assert_eq!(*number, 1.0f64); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f64); tmp += f64::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f64); tmp -= f64::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f64); tmp *= f64::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f64); tmp /= f64::NAN; }) .is_err()); assert!(panic::catch_unwind(|| { let mut tmp = not_nan(0.0f64); tmp %= f64::NAN; }) .is_err()); } #[test] fn not_nan64_zero() { assert_eq!(NotNan::::zero(), not_nan(0.0f64)); assert!(NotNan::::zero().is_zero()); } #[test] fn not_nan64_one() { assert_eq!(NotNan::::one(), not_nan(1.0f64)) } #[test] fn not_nan64_bounded() { assert_eq!(NotNan::::min_value(), ::min_value()); assert_eq!(NotNan::::max_value(), ::max_value()); } #[test] fn not_nan64_from_primitive() { assert_eq!(NotNan::::from_i8(42i8), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u8(42u8), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i16(42i16), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u16(42u16), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i32(42i32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u32(42u32), Some(not_nan(42.0))); assert_eq!(NotNan::::from_i64(42i64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_u64(42u64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_isize(42isize), Some(not_nan(42.0))); assert_eq!(NotNan::::from_usize(42usize), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(42f64), Some(not_nan(42.0))); assert_eq!(NotNan::::from_f64(Float::nan()), None); assert_eq!(NotNan::::from_f64(Float::nan()), None); } #[test] fn not_nan64_to_primitive() { let x = not_nan(42.0f64); assert_eq!(x.to_u8(), Some(42u8)); assert_eq!(x.to_i8(), Some(42i8)); assert_eq!(x.to_u16(), Some(42u16)); assert_eq!(x.to_i16(), Some(42i16)); assert_eq!(x.to_u32(), Some(42u32)); assert_eq!(x.to_i32(), Some(42i32)); assert_eq!(x.to_u64(), Some(42u64)); assert_eq!(x.to_i64(), Some(42i64)); assert_eq!(x.to_usize(), Some(42usize)); assert_eq!(x.to_isize(), Some(42isize)); assert_eq!(x.to_f64(), Some(42f64)); assert_eq!(x.to_f64(), Some(42f64)); assert_eq!(x.to_f64(), Some(42f64)); assert_eq!(x.to_f64(), Some(42f64)); } #[test] fn not_nan64_num() { assert_eq!( NotNan::::from_str_radix("42.0", 10).unwrap(), not_nan(42.0f64) ); assert!(NotNan::::from_str_radix("NaN", 10).is_err()); } #[test] fn not_nan64_signed() { assert_eq!(not_nan(42f64).abs(), not_nan(42f64)); assert_eq!(not_nan(-42f64).abs(), not_nan(42f64)); assert_eq!(not_nan(50f64).abs_sub(¬_nan(8f64)), not_nan(42f64)); assert_eq!(not_nan(8f64).abs_sub(¬_nan(50f64)), not_nan(0f64)); } #[test] fn not_nan64_num_cast() { assert_eq!( as num_traits::NumCast>::from(42), Some(not_nan(42f64)) ); assert_eq!( as num_traits::NumCast>::from(f64::nan()), None); } #[test] fn hash_zero_and_neg_zero_to_the_same_hc() { let state = RandomState::new(); let mut h1 = state.build_hasher(); let mut h2 = state.build_hasher(); OrderedFloat::from(0f64).hash(&mut h1); OrderedFloat::from(-0f64).hash(&mut h2); assert_eq!(h1.finish(), h2.finish()); } #[test] fn hash_inf_and_neg_inf_to_different_hcs() { let state = RandomState::new(); let mut h1 = state.build_hasher(); let mut h2 = state.build_hasher(); OrderedFloat::from(f64::INFINITY).hash(&mut h1); OrderedFloat::from(f64::NEG_INFINITY).hash(&mut h2); assert!(h1.finish() != h2.finish()); } #[test] fn hash_is_good_for_whole_numbers() { let state = RandomState::new(); let limit = 10000; let mut set = ::std::collections::HashSet::with_capacity(limit); for i in 0..limit { let mut h = state.build_hasher(); OrderedFloat::from(i as f64).hash(&mut h); set.insert(h.finish()); } // This allows 100 collisions, which is far too // many, but should guard against transient issues // that will result from using RandomState let pct_unique = set.len() as f64 / limit as f64; assert!(0.99f64 < pct_unique, "percent-unique={}", pct_unique); } #[test] fn hash_is_good_for_fractional_numbers() { let state = RandomState::new(); let limit = 10000; let mut set = ::std::collections::HashSet::with_capacity(limit); for i in 0..limit { let mut h = state.build_hasher(); OrderedFloat::from(i as f64 * (1f64 / limit as f64)).hash(&mut h); set.insert(h.finish()); } // This allows 100 collisions, which is far too // many, but should guard against transient issues // that will result from using RandomState let pct_unique = set.len() as f64 / limit as f64; assert!(0.99f64 < pct_unique, "percent-unique={}", pct_unique); } #[test] #[should_panic] fn test_add_fails_on_nan() { let a = not_nan(std::f32::INFINITY); let b = not_nan(std::f32::NEG_INFINITY); let _c = a + b; } #[test] #[should_panic] fn test_add_fails_on_nan_ref() { let a = not_nan(std::f32::INFINITY); let b = not_nan(std::f32::NEG_INFINITY); let _c = a + &b; } #[test] #[should_panic] fn test_add_fails_on_nan_ref_ref() { let a = not_nan(std::f32::INFINITY); let b = not_nan(std::f32::NEG_INFINITY); let _c = &a + &b; } #[test] #[should_panic] fn test_add_fails_on_nan_t_ref() { let a = not_nan(std::f32::INFINITY); let b = std::f32::NEG_INFINITY; let _c = a + &b; } #[test] #[should_panic] fn test_add_fails_on_nan_ref_t_ref() { let a = not_nan(std::f32::INFINITY); let b = std::f32::NEG_INFINITY; let _c = &a + &b; } #[test] #[should_panic] fn test_add_fails_on_nan_ref_t() { let a = not_nan(std::f32::INFINITY); let b = std::f32::NEG_INFINITY; let _c = &a + b; } #[test] #[should_panic] fn test_add_assign_fails_on_nan_ref() { let mut a = not_nan(std::f32::INFINITY); let b = not_nan(std::f32::NEG_INFINITY); a += &b; } #[test] #[should_panic] fn test_add_assign_fails_on_nan_t_ref() { let mut a = not_nan(std::f32::INFINITY); let b = std::f32::NEG_INFINITY; a += &b; } #[test] #[should_panic] fn test_add_assign_fails_on_nan_t() { let mut a = not_nan(std::f32::INFINITY); let b = std::f32::NEG_INFINITY; a += b; } #[test] fn add() { assert_eq!(not_nan(0.0) + not_nan(0.0), 0.0); assert_eq!(not_nan(0.0) + ¬_nan(0.0), 0.0); assert_eq!(¬_nan(0.0) + not_nan(0.0), 0.0); assert_eq!(¬_nan(0.0) + ¬_nan(0.0), 0.0); assert_eq!(not_nan(0.0) + 0.0, 0.0); assert_eq!(not_nan(0.0) + &0.0, 0.0); assert_eq!(¬_nan(0.0) + 0.0, 0.0); assert_eq!(¬_nan(0.0) + &0.0, 0.0); assert_eq!(OrderedFloat(0.0) + OrderedFloat(0.0), 0.0); assert_eq!(OrderedFloat(0.0) + &OrderedFloat(0.0), 0.0); assert_eq!(&OrderedFloat(0.0) + OrderedFloat(0.0), 0.0); assert_eq!(&OrderedFloat(0.0) + &OrderedFloat(0.0), 0.0); assert_eq!(OrderedFloat(0.0) + 0.0, 0.0); assert_eq!(OrderedFloat(0.0) + &0.0, 0.0); assert_eq!(&OrderedFloat(0.0) + 0.0, 0.0); assert_eq!(&OrderedFloat(0.0) + &0.0, 0.0); } #[test] fn ordered_f32_neg() { assert_eq!(OrderedFloat(-7.0f32), -OrderedFloat(7.0f32)); } #[test] fn ordered_f64_neg() { assert_eq!(OrderedFloat(-7.0f64), -OrderedFloat(7.0f64)); } #[test] #[should_panic] fn test_sum_fails_on_nan() { let a = not_nan(std::f32::INFINITY); let b = not_nan(std::f32::NEG_INFINITY); let _c: NotNan<_> = [a, b].iter().sum(); } #[test] #[should_panic] fn test_product_fails_on_nan() { let a = not_nan(std::f32::INFINITY); let b = not_nan(0f32); let _c: NotNan<_> = [a, b].iter().product(); } #[test] fn not_nan64_sum_product() { let a = not_nan(2138.1237); let b = not_nan(132f64); let c = not_nan(5.1); assert_eq!( std::iter::empty::>().sum::>(), NotNan::new(0f64).unwrap() ); assert_eq!([a].iter().sum::>(), a); assert_eq!([a, b].iter().sum::>(), a + b); assert_eq!([a, b, c].iter().sum::>(), a + b + c); assert_eq!( std::iter::empty::>().product::>(), NotNan::new(1f64).unwrap() ); assert_eq!([a].iter().product::>(), a); assert_eq!([a, b].iter().product::>(), a * b); assert_eq!([a, b, c].iter().product::>(), a * b * c); } #[test] fn not_nan_usage_in_const_context() { const A: NotNan = unsafe { NotNan::new_unchecked(111f32) }; assert_eq!(A, NotNan::new(111f32).unwrap()); } #[test] fn not_nan_panic_safety() { let catch_op = |mut num, op: fn(&mut NotNan<_>)| { let mut num_ref = panic::AssertUnwindSafe(&mut num); let _ = panic::catch_unwind(move || op(&mut *num_ref)); num }; assert!(!catch_op(not_nan(f32::INFINITY), |a| *a += f32::NEG_INFINITY).is_nan()); assert!(!catch_op(not_nan(f32::INFINITY), |a| *a -= f32::INFINITY).is_nan()); assert!(!catch_op(not_nan(0.0), |a| *a *= f32::INFINITY).is_nan()); assert!(!catch_op(not_nan(0.0), |a| *a /= 0.0).is_nan()); assert!(!catch_op(not_nan(0.0), |a| *a %= 0.0).is_nan()); } #[test] fn from_ref() { let f = 1.0f32; let o: &OrderedFloat = (&f).into(); assert_eq!(*o, 1.0f32); let mut f = 1.0f64; let o: &OrderedFloat = (&f).into(); assert_eq!(*o, 1.0f64); let o: &mut OrderedFloat = (&mut f).into(); assert_eq!(*o, 1.0f64); *o = OrderedFloat(2.0); assert_eq!(*o, 2.0f64); assert_eq!(f, 2.0f64); } #[cfg(feature = "arbitrary")] mod arbitrary_test { use super::{NotNan, OrderedFloat}; use arbitrary::{Arbitrary, Unstructured}; #[test] fn exhaustive() { // Exhaustively search all patterns of sign and exponent bits plus a few mantissa bits. for high_bytes in 0..=u16::MAX { let [h1, h2] = high_bytes.to_be_bytes(); // Each of these should not // * panic, // * return an error, or // * need more bytes than given. let n32: NotNan = Unstructured::new(&[h1, h2, h1, h2]) .arbitrary() .expect("NotNan failure"); let n64: NotNan = Unstructured::new(&[h1, h2, h1, h2, h1, h2, h1, h2]) .arbitrary() .expect("NotNan failure"); let _: OrderedFloat = Unstructured::new(&[h1, h2, h1, h2]) .arbitrary() .expect("OrderedFloat failure"); let _: OrderedFloat = Unstructured::new(&[h1, h2, h1, h2, h1, h2, h1, h2]) .arbitrary() .expect("OrderedFloat failure"); // Check for violation of NotNan's property of never containing a NaN. assert!(!n32.into_inner().is_nan()); assert!(!n64.into_inner().is_nan()); } } #[test] fn size_hints() { assert_eq!(NotNan::::size_hint(0), (4, Some(4))); assert_eq!(NotNan::::size_hint(0), (8, Some(8))); assert_eq!(OrderedFloat::::size_hint(0), (4, Some(4))); assert_eq!(OrderedFloat::::size_hint(0), (8, Some(8))); } }