unsigned_f64/
unsigned.rs

1use std::{
2    fmt::Display,
3    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub},
4};
5
6#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Default)]
7/// a float that is guaranteed to be positive or zero
8pub struct UnsignedF64(f64);
9
10impl UnsignedF64 {
11    /// Creates a new UnsignedF64 if the value is positive or zero.
12    /// Returns None if the value is negative.
13    /// Note: This converts -0.0 to 0.0 as -0.0 can introduce
14    ///     negative values in some operations that are otherwise safe.
15    pub fn new(value: f64) -> Option<UnsignedF64> {
16        if value == 0.0_f64 {
17            Some(UnsignedF64(0.0))
18        } else if value >= 0.0_f64 {
19            Some(UnsignedF64(value))
20        } else {
21            None
22        }
23    }
24
25    pub fn square(f: f64) -> UnsignedF64 {
26        UnsignedF64(f * f)
27    }
28
29    // f64 methods
30
31    pub fn floor(&self) -> UnsignedF64 {
32        UnsignedF64(self.0.floor())
33    }
34
35    pub fn ceil(&self) -> UnsignedF64 {
36        UnsignedF64(self.0.ceil())
37    }
38
39    pub fn round(&self) -> UnsignedF64 {
40        UnsignedF64(self.0.round())
41    }
42
43    pub fn trunc(&self) -> UnsignedF64 {
44        UnsignedF64(self.0.trunc())
45    }
46
47    pub fn fract(&self) -> UnsignedF64 {
48        UnsignedF64(self.0.fract())
49    }
50
51    pub fn abs(&self) -> UnsignedF64 {
52        UnsignedF64(self.0.abs())
53    }
54
55    /// This can return -1 in the case of -0.0.
56    /// -0.0 is valid for UnsignedF64 since its zero, but signum considers it negative.
57    pub fn signum(&self) -> f64 {
58        self.0.signum()
59    }
60
61    /// This can return a negative if sign is -0.0.
62    /// -0.0 is valid for UnsignedF64 since its zero, but copysign considers it negative.
63    pub fn copysign(&self, sign: f64) -> f64 {
64        self.0.copysign(sign)
65    }
66
67    pub fn mul_add(&self, a: UnsignedF64, b: UnsignedF64) -> UnsignedF64 {
68        UnsignedF64(self.0.mul_add(a.0, b.0))
69    }
70
71    // // TODO: I'm not sure if div_euclid guarantees a non-negative result.
72    // // change this to UnsignedF64 if it does.
73    // pub fn div_euclid(&self, rhs: UnsignedF64) -> f64 {
74    //     self.0.div_euclid(rhs.0)
75    // }
76
77    pub fn rem_euclid(&self, rhs: UnsignedF64) -> UnsignedF64 {
78        UnsignedF64(self.0.rem_euclid(rhs.0))
79    }
80
81    pub fn powi(&self, n: i32) -> UnsignedF64 {
82        UnsignedF64(self.0.powi(n))
83    }
84
85    pub fn powf(&self, n: f64) -> UnsignedF64 {
86        UnsignedF64(self.0.powf(n))
87    }
88
89    /// This should not return NaN.
90    pub fn sqrt(&self) -> UnsignedF64 {
91        UnsignedF64(self.0.sqrt())
92    }
93
94    pub fn exp(&self) -> UnsignedF64 {
95        UnsignedF64(self.0.exp())
96    }
97
98    pub fn exp2(&self) -> UnsignedF64 {
99        UnsignedF64(self.0.exp2())
100    }
101
102    pub fn ln(&self) -> f64 {
103        self.0.ln()
104    }
105
106    pub fn log(&self, base: f64) -> f64 {
107        self.0.log(base)
108    }
109
110    pub fn log2(&self) -> f64 {
111        self.0.log2()
112    }
113
114    pub fn log10(&self) -> f64 {
115        self.0.log10()
116    }
117
118    pub fn cbrt(&self) -> UnsignedF64 {
119        UnsignedF64(self.0.cbrt())
120    }
121
122    pub fn hypot(&self, other: f64) -> UnsignedF64 {
123        UnsignedF64(self.0.hypot(other))
124    }
125
126    pub fn sin(&self) -> f64 {
127        self.0.sin()
128    }
129
130    pub fn cos(&self) -> f64 {
131        self.0.cos()
132    }
133
134    pub fn tan(&self) -> f64 {
135        self.0.tan()
136    }
137
138    pub fn asin(&self) -> UnsignedF64 {
139        UnsignedF64(self.0.asin())
140    }
141
142    pub fn acos(&self) -> UnsignedF64 {
143        UnsignedF64(self.0.acos())
144    }
145
146    pub fn atan(&self) -> UnsignedF64 {
147        UnsignedF64(self.0.atan())
148    }
149
150    // TODO: can this be UnsignedF64?
151    // pub fn atan2(&self, other: f64) -> UnsignedF64 {
152    //     UnsignedF64(self.0.atan2(other))
153    // }
154
155    pub fn sin_cos(&self) -> (f64, f64) {
156        self.0.sin_cos()
157    }
158
159    pub fn exp_m1(&self) -> UnsignedF64 {
160        UnsignedF64(self.0.exp_m1())
161    }
162
163    pub fn ln_1p(&self) -> UnsignedF64 {
164        UnsignedF64(self.0.ln_1p())
165    }
166
167    pub fn sinh(&self) -> UnsignedF64 {
168        UnsignedF64(self.0.sinh())
169    }
170
171    pub fn cosh(&self) -> UnsignedF64 {
172        UnsignedF64(self.0.cosh())
173    }
174
175    pub fn tanh(&self) -> UnsignedF64 {
176        UnsignedF64(self.0.tanh())
177    }
178
179    pub fn asinh(&self) -> UnsignedF64 {
180        UnsignedF64(self.0.asinh())
181    }
182
183    pub fn acosh(&self) -> UnsignedF64 {
184        UnsignedF64(self.0.acosh())
185    }
186
187    pub fn atanh(&self) -> UnsignedF64 {
188        UnsignedF64(self.0.atanh())
189    }
190
191    pub fn is_nan(&self) -> bool {
192        self.0.is_nan()
193    }
194
195    pub fn is_infinite(&self) -> bool {
196        self.0.is_infinite()
197    }
198
199    pub fn is_finite(&self) -> bool {
200        self.0.is_finite()
201    }
202
203    pub fn is_subnormal(&self) -> bool {
204        self.0.is_subnormal()
205    }
206
207    pub fn is_normal(&self) -> bool {
208        self.0.is_normal()
209    }
210
211    pub fn classify(&self) -> std::num::FpCategory {
212        self.0.classify()
213    }
214
215    /// this may be false if the value is -0.0
216    pub fn is_sign_positive(&self) -> bool {
217        self.0.is_sign_positive()
218    }
219
220    /// this may be true if the value is -0.0
221    pub fn is_sign_negative(&self) -> bool {
222        self.0.is_sign_negative()
223    }
224
225    pub fn recip(&self) -> UnsignedF64 {
226        UnsignedF64(self.0.recip())
227    }
228
229    pub fn to_degrees(&self) -> UnsignedF64 {
230        UnsignedF64(self.0.to_degrees())
231    }
232
233    pub fn to_radians(&self) -> UnsignedF64 {
234        UnsignedF64(self.0.to_radians())
235    }
236
237    pub fn max(self, other: UnsignedF64) -> UnsignedF64 {
238        UnsignedF64(self.0.max(other.0))
239    }
240
241    pub fn min(self, other: UnsignedF64) -> UnsignedF64 {
242        UnsignedF64(self.0.min(other.0))
243    }
244
245    // for to/from bits, use f64's methods
246
247    pub fn total_cmp(&self, other: &UnsignedF64) -> std::cmp::Ordering {
248        self.0.total_cmp(&other.0)
249    }
250
251    pub fn clamp(self, min: UnsignedF64, max: UnsignedF64) -> UnsignedF64 {
252        UnsignedF64(self.0.clamp(min.0, max.0))
253    }
254
255    /// Performs no checking. Useful in scenarios where you know the value is positive and you want to avoid the overhead of checking.
256    /// # Safety
257    /// The value must be positive or zero.
258    pub unsafe fn from_f64_unchecked(value: f64) -> UnsignedF64 {
259        UnsignedF64(value)
260    }
261
262    pub const PI: UnsignedF64 = UnsignedF64(std::f64::consts::PI);
263    pub const E: UnsignedF64 = UnsignedF64(std::f64::consts::E);
264    pub const FRAC_1_PI: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_1_PI);
265    pub const FRAC_1_SQRT_2: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_1_SQRT_2);
266    pub const FRAC_2_PI: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_2_PI);
267    pub const FRAC_2_SQRT_PI: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_2_SQRT_PI);
268    pub const FRAC_PI_2: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_PI_2);
269    pub const FRAC_PI_3: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_PI_3);
270    pub const FRAC_PI_4: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_PI_4);
271    pub const FRAC_PI_6: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_PI_6);
272    pub const FRAC_PI_8: UnsignedF64 = UnsignedF64(std::f64::consts::FRAC_PI_8);
273    pub const LN_10: UnsignedF64 = UnsignedF64(std::f64::consts::LN_10);
274    pub const LN_2: UnsignedF64 = UnsignedF64(std::f64::consts::LN_2);
275    pub const LOG10_E: UnsignedF64 = UnsignedF64(std::f64::consts::LOG10_E);
276    pub const LOG2_E: UnsignedF64 = UnsignedF64(std::f64::consts::LOG2_E);
277    pub const SQRT_2: UnsignedF64 = UnsignedF64(std::f64::consts::SQRT_2);
278    pub const TAU: UnsignedF64 = UnsignedF64(std::f64::consts::TAU);
279    pub const EPSILON: UnsignedF64 = UnsignedF64(f64::EPSILON);
280    pub const INFINITY: UnsignedF64 = UnsignedF64(f64::INFINITY);
281
282    pub const ZERO: UnsignedF64 = UnsignedF64(0.0);
283    pub const ONE: UnsignedF64 = UnsignedF64(1.0);
284}
285
286impl Display for UnsignedF64 {
287    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288        self.0.fmt(f)
289    }
290}
291
292impl PartialEq<f64> for UnsignedF64 {
293    fn eq(&self, other: &f64) -> bool {
294        self.0 == *other
295    }
296}
297
298impl PartialOrd<f64> for UnsignedF64 {
299    fn partial_cmp(&self, other: &f64) -> Option<std::cmp::Ordering> {
300        self.0.partial_cmp(other)
301    }
302}
303
304impl From<UnsignedF64> for f64 {
305    fn from(value: UnsignedF64) -> f64 {
306        value.0
307    }
308}
309
310impl TryFrom<f64> for UnsignedF64 {
311    type Error = (); // there's nothing to report, its just negative
312
313    fn try_from(value: f64) -> Result<Self, Self::Error> {
314        if value >= 0.0 {
315            Ok(UnsignedF64(value))
316        } else {
317            Err(())
318        }
319    }
320}
321
322impl AsRef<f64> for UnsignedF64 {
323    fn as_ref(&self) -> &f64 {
324        &self.0
325    }
326}
327
328impl AsMut<f64> for UnsignedF64 {
329    fn as_mut(&mut self) -> &mut f64 {
330        &mut self.0
331    }
332}
333
334impl Add for UnsignedF64 {
335    type Output = UnsignedF64;
336    fn add(self, other: UnsignedF64) -> UnsignedF64 {
337        UnsignedF64(self.0 + other.0)
338    }
339}
340
341impl Add<f64> for UnsignedF64 {
342    type Output = f64;
343    fn add(self, rhs: f64) -> Self::Output {
344        self.0 + rhs
345    }
346}
347
348impl Sub for UnsignedF64 {
349    type Output = f64;
350    fn sub(self, rhs: Self) -> Self::Output {
351        self.0 - rhs.0
352    }
353}
354
355impl Sub<f64> for UnsignedF64 {
356    type Output = f64;
357    fn sub(self, rhs: f64) -> Self::Output {
358        self.0 - rhs
359    }
360}
361
362impl Mul for UnsignedF64 {
363    type Output = UnsignedF64;
364    fn mul(self, rhs: Self) -> Self::Output {
365        UnsignedF64(self.0 * rhs.0)
366    }
367}
368
369impl Mul<f64> for UnsignedF64 {
370    type Output = f64;
371    fn mul(self, rhs: f64) -> Self::Output {
372        self.0 * rhs
373    }
374}
375
376impl Div for UnsignedF64 {
377    type Output = UnsignedF64;
378    fn div(self, rhs: Self) -> Self::Output {
379        UnsignedF64(self.0 / rhs.0)
380    }
381}
382
383impl Div<f64> for UnsignedF64 {
384    type Output = f64;
385    fn div(self, rhs: f64) -> Self::Output {
386        self.0 / rhs
387    }
388}
389
390impl Neg for UnsignedF64 {
391    type Output = f64;
392    fn neg(self) -> Self::Output {
393        -self.0
394    }
395}
396
397impl AddAssign for UnsignedF64 {
398    fn add_assign(&mut self, rhs: Self) {
399        self.0 += rhs.0;
400    }
401}
402
403impl MulAssign for UnsignedF64 {
404    fn mul_assign(&mut self, rhs: Self) {
405        self.0 *= rhs.0;
406    }
407}
408
409impl DivAssign for UnsignedF64 {
410    fn div_assign(&mut self, rhs: Self) {
411        self.0 /= rhs.0;
412    }
413}