#![no_std]
#[cfg(feature = "serde")]
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
pub mod checkers;
mod float_impl;
pub mod types;
pub mod prelude {
pub use crate::types::*;
#[doc(no_inline)]
pub use num_traits::Float;
}
use core::{fmt, marker::PhantomData};
use num_traits::Float;
pub trait FloatChecker<F> {
fn check(value: F) -> bool;
fn assert(value: F);
}
#[repr(transparent)]
pub struct NoisyFloat<F: Float, C: FloatChecker<F>> {
value: F,
checker: PhantomData<C>,
}
impl<F: Float, C: FloatChecker<F>> NoisyFloat<F, C> {
#[inline]
#[track_caller]
pub fn new(value: F) -> Self {
C::assert(value);
Self::unchecked_new_generic(value)
}
#[inline]
fn unchecked_new_generic(value: F) -> Self {
NoisyFloat {
value,
checker: PhantomData,
}
}
#[inline]
pub fn try_new(value: F) -> Option<Self> {
if C::check(value) {
Some(NoisyFloat {
value,
checker: PhantomData,
})
} else {
None
}
}
#[inline]
#[track_caller]
pub fn borrowed(value: &F) -> &Self {
C::assert(*value);
Self::unchecked_borrowed(value)
}
#[inline]
fn unchecked_borrowed(value: &F) -> &Self {
unsafe { &*(value as *const F as *const Self) }
}
#[inline]
pub fn try_borrowed(value: &F) -> Option<&Self> {
if C::check(*value) {
Some(Self::unchecked_borrowed(value))
} else {
None
}
}
#[inline]
#[track_caller]
pub fn borrowed_mut(value: &mut F) -> &mut Self {
C::assert(*value);
Self::unchecked_borrowed_mut(value)
}
#[inline]
fn unchecked_borrowed_mut(value: &mut F) -> &mut Self {
unsafe { &mut *(value as *mut F as *mut Self) }
}
#[inline]
pub fn try_borrowed_mut(value: &mut F) -> Option<&mut Self> {
if C::check(*value) {
Some(Self::unchecked_borrowed_mut(value))
} else {
None
}
}
#[inline]
#[track_caller]
pub fn from_f32(value: f32) -> Self {
Self::new(F::from(value).unwrap())
}
#[inline]
#[track_caller]
pub fn from_f64(value: f64) -> Self {
Self::new(F::from(value).unwrap())
}
#[inline]
pub fn raw(self) -> F {
self.value
}
#[inline]
pub fn min(self, other: Self) -> Self {
Ord::min(self, other)
}
#[inline]
pub fn max(self, other: Self) -> Self {
Ord::max(self, other)
}
}
impl<F: Float + Default, C: FloatChecker<F>> Default for NoisyFloat<F, C> {
#[inline]
#[track_caller]
fn default() -> Self {
Self::new(F::default())
}
}
impl<F: Float + fmt::Debug, C: FloatChecker<F>> fmt::Debug for NoisyFloat<F, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&self.value, f)
}
}
impl<F: Float + fmt::Display, C: FloatChecker<F>> fmt::Display for NoisyFloat<F, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Display::fmt(&self.value, f)
}
}
impl<F: Float + fmt::LowerExp, C: FloatChecker<F>> fmt::LowerExp for NoisyFloat<F, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::LowerExp::fmt(&self.value, f)
}
}
impl<F: Float + fmt::UpperExp, C: FloatChecker<F>> fmt::UpperExp for NoisyFloat<F, C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::UpperExp::fmt(&self.value, f)
}
}
#[cfg(feature = "serde")]
impl<F: Float + Serialize, C: FloatChecker<F>> Serialize for NoisyFloat<F, C> {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
self.value.serialize(ser)
}
}
#[cfg(feature = "serde")]
impl<'de, F: Float + Deserialize<'de>, C: FloatChecker<F>> Deserialize<'de> for NoisyFloat<F, C> {
fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
let value = F::deserialize(de)?;
Self::try_new(value).ok_or_else(|| D::Error::custom("invalid NoisyFloat"))
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::prelude::v1::*;
use crate::prelude::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_json;
use std::{
f32,
f64::{self, consts},
hash::{Hash, Hasher},
mem::{align_of, size_of},
};
#[test]
fn smoke_test() {
assert_eq!(n64(1.0) + 2.0, 3.0);
assert_ne!(n64(3.0), n64(2.9));
assert!(r64(1.0) < 2.0);
let mut value = n64(18.0);
value %= n64(5.0);
assert_eq!(-value, n64(-3.0));
assert_eq!(r64(1.0).exp(), consts::E);
assert_eq!((N64::try_new(1.0).unwrap() / N64::infinity()), 0.0);
assert_eq!(N64::from_f32(f32::INFINITY), N64::from_f64(f64::INFINITY));
assert_eq!(R64::try_new(f64::NEG_INFINITY), None);
assert_eq!(N64::try_new(f64::NAN), None);
assert_eq!(R64::try_new(f64::NAN), None);
assert_eq!(N64::try_borrowed(&f64::NAN), None);
let mut nan = f64::NAN;
assert_eq!(N64::try_borrowed_mut(&mut nan), None);
}
#[test]
fn ensure_layout() {
assert_eq!(size_of::<N32>(), size_of::<f32>());
assert_eq!(align_of::<N32>(), align_of::<f32>());
assert_eq!(size_of::<N64>(), size_of::<f64>());
assert_eq!(align_of::<N64>(), align_of::<f64>());
}
#[test]
fn borrowed_casts() {
assert_eq!(R64::borrowed(&3.12), &3.12);
assert_eq!(N64::borrowed(&[f64::INFINITY; 2][0]), &f64::INFINITY);
assert_eq!(N64::borrowed_mut(&mut 2.72), &mut 2.72);
}
#[test]
fn test_convert() {
assert_eq!(f32::from(r32(3.0)), 3.0f32);
assert_eq!(f64::from(r32(5.0)), 5.0f64);
assert_eq!(f64::from(r64(7.0)), 7.0f64);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic]
fn n64_nan() {
let _ = n64(0.0) / n64(0.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic]
fn r64_nan() {
let _ = r64(0.0) / r64(0.0);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic]
fn r64_infinity() {
let _ = r64(1.0) / r64(0.0);
}
#[test]
fn resolves_min_max() {
assert_eq!(r64(1.0).min(r64(3.0)), r64(1.0));
assert_eq!(r64(1.0).max(r64(3.0)), r64(3.0));
}
#[test]
fn epsilon() {
assert_eq!(R32::epsilon(), f32::EPSILON);
assert_eq!(R64::epsilon(), f64::EPSILON);
}
#[test]
fn test_try_into() {
use std::convert::{TryFrom, TryInto};
let _: R64 = 1.0.try_into().unwrap();
let _ = R64::try_from(f64::INFINITY).unwrap_err();
}
struct TestHasher {
bytes: Vec<u8>,
}
impl Hasher for TestHasher {
fn finish(&self) -> u64 {
panic!("unexpected Hasher.finish invocation")
}
fn write(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes)
}
}
fn hash_bytes<T: Hash>(value: T) -> Vec<u8> {
let mut hasher = TestHasher { bytes: Vec::new() };
value.hash(&mut hasher);
hasher.bytes
}
#[test]
fn test_hash() {
assert_eq!(hash_bytes(r64(10.3)), hash_bytes(10.3f64.to_bits()));
assert_ne!(hash_bytes(r64(10.3)), hash_bytes(10.4f64.to_bits()));
assert_eq!(hash_bytes(r32(10.3)), hash_bytes(10.3f32.to_bits()));
assert_ne!(hash_bytes(r32(10.3)), hash_bytes(10.4f32.to_bits()));
assert_eq!(
hash_bytes(N64::infinity()),
hash_bytes(f64::INFINITY.to_bits())
);
assert_eq!(
hash_bytes(N64::neg_infinity()),
hash_bytes(f64::NEG_INFINITY.to_bits())
);
assert_eq!(hash_bytes(r64(0.0)), hash_bytes(0.0f64.to_bits()));
assert_eq!(hash_bytes(r64(-0.0)), hash_bytes(0.0f64.to_bits()));
assert_eq!(hash_bytes(r32(0.0)), hash_bytes(0.0f32.to_bits()));
assert_eq!(hash_bytes(r32(-0.0)), hash_bytes(0.0f32.to_bits()));
}
#[cfg(feature = "serde")]
#[test]
fn serialize_transparently_as_float() {
let num = R32::new(3.14);
let should_be = "3.14";
let got = serde_json::to_string(&num).unwrap();
assert_eq!(got, should_be);
}
#[cfg(feature = "serde")]
#[test]
fn deserialize_transparently_as_float() {
let src = "3.14";
let should_be = R32::new(3.14);
let got: R32 = serde_json::from_str(src).unwrap();
assert_eq!(got, should_be);
}
#[cfg(feature = "serde")]
#[test]
fn deserialize_invalid_float() {
use crate::{FloatChecker, NoisyFloat};
struct PositiveChecker;
impl FloatChecker<f64> for PositiveChecker {
fn check(value: f64) -> bool {
value > 0.
}
fn assert(value: f64) {
debug_assert!(Self::check(value))
}
}
let src = "-1.0";
let got: Result<NoisyFloat<f64, PositiveChecker>, _> = serde_json::from_str(src);
assert!(got.is_err());
}
#[cfg(feature = "serde")]
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Dummy {
value: N64,
}
#[cfg(feature = "serde")]
#[test]
fn deserialize_struct_containing_n64() {
let src = r#"{ "value": 3.14 }"#;
let should_be = Dummy { value: n64(3.14) };
let got: Dummy = serde_json::from_str(src).unwrap();
assert_eq!(got, should_be);
}
#[cfg(feature = "serde")]
#[test]
fn serialize_struct_containing_n64() {
let src = Dummy { value: n64(3.14) };
let should_be = r#"{"value":3.14}"#;
let got = serde_json::to_string(&src).unwrap();
assert_eq!(got, should_be);
}
#[cfg(feature = "approx")]
#[test]
fn approx_assert_eq() {
use approx::{assert_abs_diff_eq, assert_relative_eq, assert_ulps_eq};
let lhs = r64(0.1000000000000001);
let rhs = r64(0.1);
assert_abs_diff_eq!(lhs, rhs);
assert_relative_eq!(lhs, rhs);
assert_ulps_eq!(lhs, rhs);
}
#[test]
fn const_functions() {
const A: N32 = N32::unchecked_new(1.0);
const B: N64 = N64::unchecked_new(2.0);
const C: R32 = R32::unchecked_new(3.0);
const D: R64 = R64::unchecked_new(4.0);
const A_RAW: f32 = A.const_raw();
const B_RAW: f64 = B.const_raw();
const C_RAW: f32 = C.const_raw();
const D_RAW: f64 = D.const_raw();
assert_eq!(A_RAW, 1.0);
assert_eq!(B_RAW, 2.0);
assert_eq!(C_RAW, 3.0);
assert_eq!(D_RAW, 4.0);
}
}