1use crate::IntoInner;
2
3#[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
12pub 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 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 pub const unsafe fn unchecked(val: F) -> Self {
36 union Transmute<F: IsNan> {
37 val: F,
38 real: Real<F>,
39 }
40
41 Transmute { val }.real
43 }
44 #[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); 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); 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)); 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}