real_float/
real.rs

1use crate::IntoInner;
2
3/// The error produced when NaN is encountered.
4#[derive(Debug, Clone, Copy)]
5pub struct NanError;
6impl std::fmt::Display for NanError {
7    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
8        write!(f, "encountered NaN unexpectedly")
9    }
10}
11
12/// Trait for a floating point number that can be checked for NaN (not-a-number).
13pub trait IsNan: Sized + Copy {
14    fn is_nan(self) -> bool;
15}
16
17#[derive(Debug, Clone, Copy, Default)]
18#[repr(transparent)]
19pub struct Real<F: IsNan>(F);
20
21impl<F: IsNan> Real<F> {
22    /// Attempts to create a new `Real` float.
23    /// # Errors
24    /// If the value is NaN.
25    pub fn try_new(val: F) -> Result<Self, NanError> {
26        if val.is_nan() {
27            Err(NanError)
28        } else {
29            Ok(Self(val))
30        }
31    }
32    /// Const-safe constructor for `Real` that never checks the value.
33    /// # Safety
34    /// Ensure that the value can never be `NaN`.
35    pub const unsafe fn unchecked(val: F) -> Self {
36        union Transmute<F: IsNan> {
37            val: F,
38            real: Real<F>,
39        }
40
41        // SAFETY: `Real` is `repr(transparent)`.
42        Transmute { val }.real
43    }
44    /// Gets the inner value of this number.
45    #[inline]
46    pub const fn val(self) -> F {
47        self.0
48    }
49}
50
51ctor_impls!(Real<F: IsNan>, "If the number is NaN.");
52
53impl<F: IsNan> IntoInner<F> for Real<F> {
54    #[inline]
55    fn into_inner(self) -> F {
56        self.val()
57    }
58}
59
60eq_impls!(Real<F: IsNan>);
61ord_impls!(Real<F: IsNan>);
62round_impls!(Real<F: IsNan>);
63signed_impls!(Real<F: IsNan>);
64sum_impls!(Real<F: IsNan>, NanError, "If the result is NaN.");
65neg_impls!(Real<F: IsNan>, NanError, "If the result is NaN.");
66product_impls!(Real<F: IsNan>, NanError, "If the result is NaN.");
67impl<F: IsNan + crate::ops::Pow> Real<F> {
68    pow_methods!(F, NanError, "If the result is NaN.");
69    recip_methods!(F); // recip is infallible for real numbers
70    sqrt_methods!(F, NanError, "If the result is NaN.");
71    cbrt_methods!(F);
72    hypot_methods!(F, NanError, "If the result is NaN.");
73}
74exp_impls!(Real<F: IsNan>, NanError, "If the result is NaN.");
75impl<F: IsNan + crate::ops::Trig> Real<F> {
76    sin_cos_methods!(
77        F,
78        NanError,
79        "If the output is NaN (caused if the input is `±infinity`)."
80    );
81    tan_methods!(
82        F,
83        NanError,
84        "If the result is NaN (caused if the input is `±infinity`)."
85    );
86    asin_acos_methods!(
87        F,
88        NanError,
89        "If the output is NaN (caused if the magnitude of the input exceeds 1)."
90    );
91    atan_methods!(F); // atan always succeeds for real inputs.
92    atan2_methods!(F, NanError, "If the output is NaN.");
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    macro_rules! real {
100        ($f: expr) => {
101            Real::new($f)
102        };
103    }
104
105    #[test]
106    #[should_panic]
107    fn assert_new_nan() {
108        real!(f32::NAN);
109    }
110    #[test]
111    #[should_panic]
112    fn assert_new_nan2() {
113        real!(-f32::NAN);
114    }
115
116    #[test]
117    fn unchecked() {
118        let real = unsafe { Real::unchecked(f32::NAN) };
119        assert!(real.val().is_nan());
120    }
121
122    #[test]
123    fn assert_nan() {
124        assert_err!(real!(f32::INFINITY).try_add(f32::NEG_INFINITY));
125        assert_err!(real!(f32::INFINITY).try_sub(f32::INFINITY));
126        assert_err!(real!(0.0f32).try_mul(f32::INFINITY));
127        assert_err!(real!(0.0f32).try_div(0.0));
128        assert_err!(real!(f32::INFINITY).try_rem(1.0));
129        assert_err!(real!(1.0f32).try_rem(0.0));
130
131        assert_err!(real!(-1.0f32).try_sqrt());
132
133        assert_err!(real!(-1.0f32).try_log(3.0));
134        assert_err!(real!(-1.0f32).try_ln());
135        assert_err!(real!(-1.0f32).try_log2());
136        assert_err!(real!(-1.0f32).try_log10());
137
138        assert_err!(real!(f32::INFINITY).try_sin());
139        assert_err!(real!(f32::INFINITY).try_cos());
140        assert_err!(real!(f32::INFINITY).try_tan());
141    }
142
143    #[test]
144    fn assert_ops() {
145        assert_eq!(real!(2.0f32) + 1.0, real!(3.0));
146        assert_eq!(real!(2.0f32) - 1.0, real!(1.0));
147        assert_eq!(real!(5.0f32) * 2.0, real!(10.0));
148        assert_eq!(real!(8.0f32) / 2.0, real!(4.0));
149        assert_eq!(-real!(1.0f32), real!(-1.0));
150    }
151
152    #[test]
153    #[allow(clippy::bool_assert_comparison)]
154    #[allow(clippy::cmp_nan)]
155    fn assert_cmp_weird() {
156        assert!(real!(f32::NEG_INFINITY) < real!(-1.0));
157        assert!(real!(-1.0f32) < real!(0.0));
158
159        assert_eq!(real!(0.0f32), real!(0.0));
160        assert_eq!(real!(0.0f32), real!(-0.0));
161        assert_eq!(real!(-0.0f32), real!(0.0));
162        assert_eq!(real!(-0.0f32), real!(-0.0));
163
164        assert!(real!(0.0) < real!(1.0));
165        assert!(real!(1.0) < real!(f32::INFINITY));
166
167        assert_eq!(real!(1.0) < f32::NAN, false);
168        assert_eq!(real!(1.0) >= f32::NAN, false);
169    }
170
171    #[test]
172    fn assert_pow() {
173        assert_eq!(real!(1000.0f32).powf(1000.0), real!(f32::INFINITY));
174        assert_eq!(real!(4.0f32).powf(3.5), real!(128.0));
175        assert_eq!(real!(2.0f32).powi(8), real!(256.0));
176        assert_eq!(real!(2.0f32).recip(), real!(0.5));
177        assert_eq!(real!(4.0f32).sqrt(), real!(2.0));
178        assert_eq!(real!(27.0f32).cbrt(), real!(3.0));
179    }
180
181    #[test]
182    fn assert_exp() {
183        assert_epsilon!(real!(2.0f32).exp(), real!(7.389_056));
184        assert_epsilon!(real!(3.0f32).exp2(), real!(8.0));
185        assert_epsilon!(real!(5.0f32).exp_m1(), real!(147.413_16));
186        assert_epsilon!(real!(16.0f32).log(4.0), real!(2.0));
187        assert_epsilon!(real!(1.0f32).ln(), real!(0.0));
188        assert_epsilon!(real!(8.0f32).log2(), real!(3.0));
189        assert_epsilon!(real!(1000.0f32).log10(), real!(3.0));
190        assert_epsilon!(real!(147.413_16f32).ln_1p(), real!(5.0));
191    }
192
193    #[test]
194    fn assert_trig() {
195        use std::f32::consts::{FRAC_1_SQRT_2, PI};
196
197        assert_epsilon!(real!(0.0f32).sin(), real!(0.0));
198        assert_epsilon!(real!(PI / 4.0).sin(), real!(FRAC_1_SQRT_2));
199        assert_epsilon!(real!(PI / 2.0).sin(), real!(1.0));
200
201        assert_epsilon!(real!(0.0f32).cos(), real!(1.0));
202        assert_epsilon!(real!(PI / 4.0).cos(), real!(FRAC_1_SQRT_2));
203        assert_epsilon!(real!(PI / 2.0).cos(), 0.0);
204
205        assert_epsilon!(real!(0.0f32).tan(), real!(0.0));
206        assert_epsilon!(real!(PI / 4.0).tan(), real!(1.0));
207        assert!(real!(PI / 2.0 - f32::EPSILON).tan() > real!(2_000_000.0)); // its big
208
209        assert_epsilon!(real!(0.0f32).asin(), real!(0.0));
210        assert_epsilon!(real!(FRAC_1_SQRT_2).asin(), real!(PI / 4.0));
211        assert_epsilon!(real!(1.0f32).asin(), real!(PI / 2.0));
212
213        assert_epsilon!(real!(0.0f32).acos(), real!(PI / 2.0));
214        assert_epsilon!(real!(FRAC_1_SQRT_2).acos(), real!(PI / 4.0));
215        assert_epsilon!(real!(1.0f32).acos(), real!(0.0));
216
217        assert_epsilon!(real!(0.0f32).atan(), real!(0.0));
218        assert_epsilon!(real!(1.0f32).atan(), real!(PI / 4.0));
219        assert_epsilon!(real!(f32::INFINITY).atan(), real!(PI / 2.0));
220    }
221}