1#![deny(warnings)]
2
3#[cfg(all(feature = "low_precision", feature = "high_precision"))]
6compile_error!(
7 "feature \"low_precision\" and feature \"high_precision\" cannot be enabled at the same time"
8);
9
10#[cfg(not(feature = "fixed_precision"))]
11mod precision {
12 use types::FType;
13
14 use crate::traits::Approximately;
15 use crate::traits::FloatExt;
16 use crate::traits::FromLossy;
17
18 #[cfg(feature = "low_precision")]
19 pub(crate) mod types {
20 pub type FType = f32;
21 pub type IType = i32;
22 pub type UType = u32;
23 }
24
25 #[cfg(feature = "high_precision")]
26 pub(crate) mod types {
27 pub type FType = f64;
28 pub type IType = i64;
29 pub type UType = u64;
30 }
31
32 impl FloatExt for FType {
33 const ONE: Self = 1.0;
34 const ZERO: Self = 0.0;
35 const EPSILON: Self = FType::EPSILON;
36 }
37
38 macro_rules! from_lossy_impl {
39 ($A:ty, $B:ty) => {
40 impl FromLossy<$A> for $B {
41 fn from_lossy(value: $A) -> Self {
42 value as $B
43 }
44 }
45 };
46 }
47
48 from_lossy_impl!(FType, i8);
49 from_lossy_impl!(FType, i32);
50 from_lossy_impl!(FType, i64);
51
52 from_lossy_impl!(FType, u8);
53 from_lossy_impl!(FType, u32);
54 from_lossy_impl!(FType, u64);
55
56 from_lossy_impl!(u8, FType);
57 from_lossy_impl!(u32, FType);
58 from_lossy_impl!(u64, FType);
59
60 from_lossy_impl!(i8, FType);
61 from_lossy_impl!(i32, FType);
62 from_lossy_impl!(i64, FType);
63
64 from_lossy_impl!(f64, FType);
65 from_lossy_impl!(f32, FType);
66
67 #[cfg(feature = "high_precision")]
68 from_lossy_impl!(FType, f32);
69
70 #[cfg(feature = "low_precision")]
71 from_lossy_impl!(FType, f64);
72
73 impl Approximately for FType {
74 fn approximately(&self, other: Self, epsilon: FType) -> bool {
75 if self.is_nan() || other.is_nan() {
77 return false;
78 }
79 if self == &other {
81 return true;
82 }
83 (self - other).abs() <= epsilon
85 }
86 }
87
88 impl Approximately for &FType {
89 fn approximately(&self, other: Self, epsilon: FType) -> bool {
90 FType::approximately(*self, *other, epsilon)
91 }
92 }
93
94 impl Approximately for &mut FType {
95 fn approximately(&self, other: Self, epsilon: FType) -> bool {
96 FType::approximately(*self, *other, epsilon)
97 }
98 }
99}
100
101#[cfg(feature = "fixed_precision")]
102mod precision {
103 use crate::traits::FloatExt;
104 use types::FType;
105
106 pub(crate) mod types {
107 pub type FType = crate::fixed::Fixed;
108 pub type IType = i64;
109 pub type UType = u64;
110 }
111
112 impl FloatExt for FType {
113 const ONE: Self = FType::from_const(1.0);
114 const ZERO: Self = FType::from_const(0.0);
115 const EPSILON: Self = FType::from_const(3.0 / crate::fixed::FIXED_DECIMAL as f64);
116 }
117}
118
119pub type Float = precision::types::FType;
120pub type Int = precision::types::IType;
121pub type Unsigned = precision::types::UType;
122
123pub mod bivec;
124pub mod circle;
125pub mod constant;
126pub mod fixed;
127pub mod integrate;
128pub mod line;
129pub mod matrix;
130pub mod percent;
131pub mod plane;
132pub mod point;
133pub mod rotor;
134pub mod segment;
135pub mod shape;
136pub mod sphere;
137pub mod traits;
138pub mod vec;
139
140pub use point::Point;
141pub use traits::*;
142pub use vec::Vector;
143
144impl Zero for Float {
145 fn zero() -> Self {
146 Float::from(0.0)
147 }
148}
149
150trait One {
151 fn one() -> Self;
152}
153
154impl One for Float {
155 fn one() -> Self {
156 Float::from(1.0)
157 }
158}
159
160#[cfg(test)]
161mod equality_tests {
162 use super::*;
163 use std::f64::INFINITY;
164 use std::f64::NEG_INFINITY;
165
166 #[allow(unused_imports)]
167 use std::f64::NAN;
168
169 #[cfg(feature = "fixed_precision")]
170 use crate::traits::FloatExt;
171
172 const EPSILON: Float = Float::EPSILON;
173
174 #[test]
175 fn exact_equality() {
176 let a: Float = 1.0.into();
177 let b: Float = 1.0.into();
178 assert!(a.approximately(b, Float::from(0.0)));
179 assert!(a.approximately(b, EPSILON));
180 }
181
182 #[cfg(not(feature = "fixed_precision"))]
183 #[test]
184 fn difference_equals_epsilon() {
185 let a = Float::from(1.0);
186 let b = Float::from(1.0 + 0.5);
187 assert!(a.approximately(b, Float::from(0.5)));
188 assert!(b.approximately(a, Float::from(0.5)));
189 }
190
191 #[test]
192 #[cfg(not(feature = "fixed_precision"))]
193 fn difference_exceeds_epsilon() {
194 let a = Float::from(1.0);
195 let b = Float::from(1.0 + 0.5 + f64::EPSILON);
196 assert!(!a.approximately(b, Float::from(0.5)));
197 }
198
199 #[cfg(not(feature = "fixed_precision"))]
200 #[test]
201 fn zero_edge_cases() {
202 assert!(Float::from(0.0).approximately(Float::from(0.0), Float::from(0.0)));
203 assert!(Float::from(0.0).approximately(Float::from(1e-10), Float::from(1e-5)));
204 assert!(!Float::from(0.0).approximately(Float::from(1e-5), Float::from(1e-6)));
205 }
206
207 #[cfg(not(feature = "fixed_precision"))]
208 #[test]
209 fn opposite_signs() {
210 assert!(!Float::from(5.0).approximately(Float::from(-5.0), Float::from(9.9)));
211 assert!(Float::from(5.0).approximately(Float::from(-5.0), Float::from(10.1)));
212 }
213
214 #[test]
215 fn subnormal_numbers() {
216 let min = Float::from(f64::MIN_POSITIVE);
217 let a = Float::from(min);
218 let b = Float::from(min + min / Float::from(2.0));
219 assert!(a.approximately(b, min));
220 }
221
222 #[cfg(not(feature = "fixed_precision"))]
223 #[test]
224 fn nan_handling() {
225 assert!(!Float::from(NAN).approximately(Float::from(NAN), Float::from(f64::MAX)));
226 assert!(!Float::from(NAN).approximately(Float::from(1.0), Float::from(f64::MAX)));
227 assert!(!Float::from(1.0).approximately(Float::from(NAN), Float::from(f64::MAX)));
228 }
229
230 #[cfg(not(feature = "fixed_precision"))]
231 #[test]
232 fn infinity_handling() {
233 assert!(Float::from(INFINITY).approximately(Float::from(INFINITY), Float::from(0.0)));
234 assert!(
235 !Float::from(INFINITY).approximately(Float::from(NEG_INFINITY), Float::from(f64::MAX))
236 );
237 assert!(!Float::from(INFINITY).approximately(Float::from(1.0), Float::from(f64::MAX)));
238 assert!(!Float::from(1.0).approximately(Float::from(INFINITY), Float::from(f64::MAX)));
239 }
240
241 #[cfg(not(feature = "fixed_precision"))]
242 #[test]
243 fn large_numbers() {
244 let a = Float::from(1e20);
245 let b = Float::from(1e20 + 1e15);
246 assert!(a.approximately(b, Float::from(1e16)));
247 assert!(!a.approximately(b, Float::from(1e14)));
248 }
249
250 #[cfg(not(feature = "fixed_precision"))]
251 #[test]
252 fn tiny_epsilon() {
253 let a = Float::from(1.0 + 2.0 * f64::EPSILON);
254 let b = Float::from(1.0);
255 assert!(!a.approximately(b, Float::from(f64::EPSILON)));
256 assert!(a.approximately(b, Float::from(3.0 * f64::EPSILON)));
257 }
258
259 #[test]
260 fn symmetry_property() {
261 let a = Float::from(1.0);
262 let b = Float::from(1.000_000_1);
263 assert_eq!(a.approximately(b, EPSILON), b.approximately(a, EPSILON));
264 }
265
266 #[cfg(feature = "fixed_precision")]
267 #[test]
268 fn transitive_property() {
269 let a = Float::from(1.0);
270 let b = Float::from(1.000_000_05);
271 let c = Float::from(1.000_000_1);
272 assert!(a.approximately(b, EPSILON));
273 assert!(b.approximately(c, EPSILON));
274 assert!(a.approximately(c, EPSILON));
275 }
276}