1use crate::floating_point::f32_as_2s_compliment;
8
9#[allow(missing_docs)]
10pub const SCALAR_MAX: f32 = 3.402823466e+38;
11#[allow(missing_docs)]
12pub const SCALAR_NEARLY_ZERO: f32 = 1.0 / (1 << 12) as f32;
13#[allow(missing_docs)]
14pub const SCALAR_ROOT_2_OVER_2: f32 = 0.707106781;
15
16#[allow(missing_docs)]
20pub trait Scalar {
21 fn half(self) -> Self;
22 fn ave(self, other: Self) -> Self;
23 fn sqr(self) -> Self;
24 fn invert(self) -> Self;
25 fn bound(self, min: Self, max: Self) -> Self;
26 fn is_nearly_equal(self, other: Self) -> bool;
27 fn is_nearly_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool;
28 fn is_nearly_zero(self) -> bool;
29 fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool;
30 fn almost_dequal_ulps(self, other: Self) -> bool;
31}
32
33impl Scalar for f32 {
34 fn half(self) -> f32 {
35 self * 0.5
36 }
37
38 fn ave(self, other: Self) -> f32 {
39 (self + other) * 0.5
40 }
41
42 fn sqr(self) -> f32 {
43 self * self
44 }
45
46 fn invert(self) -> f32 {
47 1.0 / self
48 }
49
50 fn bound(self, min: Self, max: Self) -> Self {
53 max.min(self).max(min)
54 }
55
56 fn is_nearly_equal(self, other: Self) -> bool {
57 (self - other).abs() <= SCALAR_NEARLY_ZERO
58 }
59
60 fn is_nearly_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool {
61 (self - other).abs() <= tolerance
62 }
63
64 fn is_nearly_zero(self) -> bool {
65 self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
66 }
67
68 fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool {
69 debug_assert!(tolerance >= 0.0);
70 self.abs() <= tolerance
71 }
72
73 fn almost_dequal_ulps(self, other: Self) -> bool {
75 const ULPS_EPSILON: i32 = 16;
76 let a_bits = f32_as_2s_compliment(self);
77 let b_bits = f32_as_2s_compliment(other);
78 a_bits < b_bits + ULPS_EPSILON && b_bits < a_bits + ULPS_EPSILON
80 }
81}
82
83#[allow(missing_docs)]
84#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
85pub trait NoStdFloat {
86 fn trunc(self) -> Self;
87 fn sqrt(self) -> Self;
88 fn abs(self) -> Self;
89 fn sin(self) -> Self;
90 fn cos(self) -> Self;
91 fn ceil(self) -> Self;
92 fn floor(self) -> Self;
93 fn round(self) -> Self;
94 fn powf(self, y: Self) -> Self;
95 fn acos(self) -> Self;
96}
97
98#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
99impl NoStdFloat for f32 {
100 fn trunc(self) -> Self {
101 libm::truncf(self)
102 }
103 fn sqrt(self) -> Self {
104 libm::sqrtf(self)
105 }
106 fn abs(self) -> Self {
107 libm::fabsf(self)
108 }
109 fn sin(self) -> Self {
110 libm::sinf(self)
111 }
112 fn cos(self) -> Self {
113 libm::cosf(self)
114 }
115 fn ceil(self) -> Self {
116 libm::ceilf(self)
117 }
118 fn floor(self) -> Self {
119 libm::floorf(self)
120 }
121 fn round(self) -> Self {
122 libm::roundf(self)
123 }
124 fn powf(self, y: Self) -> Self {
125 libm::powf(self, y)
126 }
127 fn acos(self) -> Self {
128 libm::acosf(self)
129 }
130}
131
132#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
133impl NoStdFloat for f64 {
134 fn trunc(self) -> Self {
135 libm::trunc(self)
136 }
137 fn sqrt(self) -> Self {
138 libm::sqrt(self)
139 }
140 fn abs(self) -> Self {
141 libm::fabs(self)
142 }
143 fn sin(self) -> Self {
144 libm::sin(self)
145 }
146 fn cos(self) -> Self {
147 libm::cos(self)
148 }
149 fn ceil(self) -> Self {
150 libm::ceil(self)
151 }
152 fn floor(self) -> Self {
153 libm::floor(self)
154 }
155 fn round(self) -> Self {
156 libm::round(self)
157 }
158 fn powf(self, y: Self) -> Self {
159 libm::pow(self, y)
160 }
161 fn acos(self) -> Self {
162 libm::acos(self)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn bound() {
172 assert_eq!(f32::NAN.bound(0.0, 1.0), 1.0);
173 assert_eq!(f32::INFINITY.bound(0.0, 1.0), 1.0);
174 assert_eq!(f32::NEG_INFINITY.bound(0.0, 1.0), 0.0);
175 assert_eq!(f32::EPSILON.bound(0.0, 1.0), f32::EPSILON);
176 assert_eq!(0.5.bound(0.0, 1.0), 0.5);
177 assert_eq!((-1.0).bound(0.0, 1.0), 0.0);
178 assert_eq!(2.0.bound(0.0, 1.0), 1.0);
179 }
180}