1#![forbid(unsafe_code)]
2#![no_std]
3
4#![doc = include_str!("../README.md")]
5
6use core::marker::PhantomData;
7use core::num::FpCategory;
8use core::cmp::Ordering;
9use core::fmt;
10
11pub use num_traits::float::Float;
13
14#[cfg(test)] mod test;
15
16#[cfg(feature = "serde")]
17use serde::{Serialize, Deserialize};
18
19pub trait FloatChecker<T: Float> {
21 type Error;
23
24 fn check(value: T) -> Result<T, Self::Error>;
27}
28
29macro_rules! prop_ops {
30 ($($f:ident : $t:ty),*$(,)?) => {$(
31 pub fn $f(self) -> $t {
32 self.0.$f()
33 }
34 )*}
35}
36macro_rules! noarg_ops {
37 ($($f:ident),*$(,)?) => {$(
38 pub fn $f() -> Result<Self, C::Error> {
39 Self::new(T::$f())
40 }
41 )*}
42}
43macro_rules! unary_ops {
44 ($($f:ident),*$(,)?) => {$(
45 pub fn $f(self) -> Result<Self, C::Error> {
46 Self::new(self.0.$f())
47 }
48 )*}
49}
50macro_rules! binary_ops {
51 ($($f:ident : $other:ident),*$(,)?) => {$(
52 pub fn $f(self, $other: Self) -> Result<Self, C::Error> {
53 Self::new(self.0.$f($other.0))
54 }
55 )*}
56}
57
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76pub struct CheckedFloat<T: Float, C: FloatChecker<T>>(T, PhantomData<C>);
77impl<T: Float, C: FloatChecker<T>> CheckedFloat<T, C> {
78 pub fn new(value: T) -> Result<Self, C::Error> {
79 C::check(value).map(|x| Self(x, PhantomData))
80 }
81 pub fn get(self) -> T {
82 self.0
83 }
84 prop_ops! {
85 classify: FpCategory, integer_decode: (u64, i16, i8), is_finite: bool, is_infinite: bool,
86 is_nan: bool, is_normal: bool, is_sign_negative: bool, is_sign_positive: bool,
87 is_zero: bool, is_one: bool,
88 }
89 noarg_ops! {
90 infinity, max_value, min_positive_value, min_value, nan, neg_infinity, neg_zero, zero,
91 one, epsilon,
92 }
93 unary_ops! {
94 abs, acos, acosh, asin, asinh, atan, atanh, cbrt, ceil, cos, cosh,
95 exp, exp2, exp_m1, floor, fract, ln, ln_1p, log10, log2, neg, recip,
96 round, signum, sin, sinh, sqrt, tan, tanh, trunc, to_degrees, to_radians,
97 }
98 binary_ops! {
99 abs_sub: other, add: other, atan2: other, div: other, hypot: other, log: base,
100 mul: other, powf: n, rem: other, sub: other, copysign: sign,
101 }
102 pub fn mul_add(self, a: Self, b: Self) -> Result<Self, C::Error> {
103 Self::new(self.0.mul_add(a.0, b.0))
104 }
105 pub fn powi(self, n: i32) -> Result<Self, C::Error> {
106 Self::new(self.0.powi(n))
107 }
108 pub fn sin_cos(self) -> (Result<Self, C::Error>, Result<Self, C::Error>) {
109 let (sin, cos) = self.0.sin_cos();
110 (Self::new(sin), Self::new(cos))
111 }
112}
113
114impl<T: Float, C: FloatChecker<T>> Ord for CheckedFloat<T, C> {
115 fn cmp(&self, other: &Self) -> Ordering {
116 match (self.0.is_nan(), other.0.is_nan()) {
117 (true, true) => match (self.0.is_sign_positive(), other.0.is_sign_positive()) {
118 (true, true) | (false, false) => Ordering::Equal,
119 (true, false) => Ordering::Greater,
120 (false, true) => Ordering::Less,
121 }
122 (true, false) => if self.0.is_sign_positive() { Ordering::Greater } else { Ordering::Less },
123 (false, true) => if other.0.is_sign_positive() { Ordering::Less } else { Ordering::Greater },
124 (false, false) => self.0.partial_cmp(&other.0).unwrap(),
125 }
126 }
127}
128impl<T: Float, C: FloatChecker<T>> PartialOrd for CheckedFloat<T, C> {
129 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
130 Some(self.cmp(other))
131 }
132}
133
134impl<T: Float, C: FloatChecker<T>> Eq for CheckedFloat<T, C> { }
135impl<T: Float, C: FloatChecker<T>> PartialEq for CheckedFloat<T, C> {
136 fn eq(&self, other: &Self) -> bool {
137 self.cmp(other) == Ordering::Equal
138 }
139}
140
141impl<T: Float, C: FloatChecker<T>> Copy for CheckedFloat<T, C> { }
142impl<T: Float, C: FloatChecker<T>> Clone for CheckedFloat<T, C> {
143 fn clone(&self) -> Self {
144 Self(self.0, PhantomData)
145 }
146}
147
148impl<T: Float + fmt::Debug, C: FloatChecker<T>> fmt::Debug for CheckedFloat<T, C> {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "{:?}", self.0)
151 }
152}
153impl<T: Float + fmt::Display, C: FloatChecker<T>> fmt::Display for CheckedFloat<T, C> {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "{}", self.0)
156 }
157}