1use crate::{Boolean, Double, Integer};
2use std::cmp::Ordering;
3use std::fmt;
4use std::num::ParseFloatError;
5use std::ops::{Add, Div, Mul, Neg, Sub};
6use std::str::FromStr;
7
8#[derive(Debug, Clone, Copy, Default, PartialEq)]
14#[repr(transparent)]
15pub struct Float {
16 value: f32,
17}
18
19impl Float {
20 pub const INFINITY: Self = Self {
21 value: f32::INFINITY,
22 };
23 pub const MAX: Self = Self { value: f32::MAX };
24 pub const MIN: Self = Self { value: f32::MIN };
25 pub const NAN: Self = Self { value: f32::NAN };
26 pub const NEG_INFINITY: Self = Self {
27 value: f32::NEG_INFINITY,
28 };
29
30 #[inline]
31 #[must_use]
32 pub fn from_be_bytes(bytes: [u8; 4]) -> Self {
33 Self {
34 value: f32::from_be_bytes(bytes),
35 }
36 }
37
38 #[inline]
39 #[must_use]
40 pub fn to_be_bytes(self) -> [u8; 4] {
41 self.value.to_be_bytes()
42 }
43
44 #[inline]
46 #[must_use]
47 pub fn abs(self) -> Self {
48 self.value.abs().into()
49 }
50
51 #[inline]
53 #[must_use]
54 pub fn ceil(self) -> Self {
55 self.value.ceil().into()
56 }
57
58 #[inline]
60 #[must_use]
61 pub fn floor(self) -> Self {
62 self.value.floor().into()
63 }
64
65 #[inline]
67 #[must_use]
68 pub fn round(self) -> Self {
69 self.value.round().into()
70 }
71
72 #[inline]
73 #[must_use]
74 pub fn is_nan(self) -> bool {
75 self.value.is_nan()
76 }
77
78 #[inline]
79 #[must_use]
80 pub fn is_finite(self) -> bool {
81 self.value.is_finite()
82 }
83
84 #[inline]
86 #[must_use]
87 pub fn is_identical_with(self, other: Self) -> bool {
88 self.value.to_bits() == other.value.to_bits()
89 }
90}
91
92impl From<Float> for f32 {
93 #[inline]
94 fn from(value: Float) -> Self {
95 value.value
96 }
97}
98
99impl From<Float> for f64 {
100 #[inline]
101 fn from(value: Float) -> Self {
102 value.value.into()
103 }
104}
105
106impl From<f32> for Float {
107 #[inline]
108 fn from(value: f32) -> Self {
109 Self { value }
110 }
111}
112
113impl From<i8> for Float {
114 #[inline]
115 fn from(value: i8) -> Self {
116 Self {
117 value: value.into(),
118 }
119 }
120}
121
122impl From<i16> for Float {
123 #[inline]
124 fn from(value: i16) -> Self {
125 Self {
126 value: value.into(),
127 }
128 }
129}
130
131impl From<u8> for Float {
132 #[inline]
133 fn from(value: u8) -> Self {
134 Self {
135 value: value.into(),
136 }
137 }
138}
139
140impl From<u16> for Float {
141 #[inline]
142 fn from(value: u16) -> Self {
143 Self {
144 value: value.into(),
145 }
146 }
147}
148
149impl From<Boolean> for Float {
150 #[inline]
151 fn from(value: Boolean) -> Self {
152 f32::from(bool::from(value)).into()
153 }
154}
155
156impl From<Integer> for Float {
157 #[inline]
158 #[allow(clippy::cast_precision_loss)]
159 fn from(value: Integer) -> Self {
160 (i64::from(value) as f32).into()
161 }
162}
163
164impl From<Double> for Float {
165 #[inline]
166 #[allow(clippy::cast_possible_truncation)]
167 fn from(value: Double) -> Self {
168 Self {
169 value: f64::from(value) as f32,
170 }
171 }
172}
173
174impl FromStr for Float {
175 type Err = ParseFloatError;
176
177 #[inline]
178 fn from_str(input: &str) -> Result<Self, Self::Err> {
179 Ok(f32::from_str(input)?.into())
180 }
181}
182
183impl fmt::Display for Float {
184 #[inline]
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 if self.value == f32::INFINITY {
187 f.write_str("INF")
188 } else if self.value == f32::NEG_INFINITY {
189 f.write_str("-INF")
190 } else {
191 self.value.fmt(f)
192 }
193 }
194}
195
196impl PartialOrd for Float {
197 #[inline]
198 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
199 self.value.partial_cmp(&other.value)
200 }
201}
202
203impl Neg for Float {
204 type Output = Self;
205
206 #[inline]
207 fn neg(self) -> Self {
208 (-self.value).into()
209 }
210}
211
212impl Add for Float {
213 type Output = Self;
214
215 #[inline]
216 fn add(self, rhs: Self) -> Self {
217 (self.value + rhs.value).into()
218 }
219}
220
221impl Sub for Float {
222 type Output = Self;
223
224 #[inline]
225 fn sub(self, rhs: Self) -> Self {
226 (self.value - rhs.value).into()
227 }
228}
229
230impl Mul for Float {
231 type Output = Self;
232
233 #[inline]
234 fn mul(self, rhs: Self) -> Self {
235 (self.value * rhs.value).into()
236 }
237}
238
239impl Div for Float {
240 type Output = Self;
241
242 #[inline]
243 fn div(self, rhs: Self) -> Self {
244 (self.value / rhs.value).into()
245 }
246}
247
248#[cfg(test)]
249#[allow(clippy::panic_in_result_fn)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn eq() {
255 assert_eq!(Float::from(0.), Float::from(0.));
256 assert_ne!(Float::NAN, Float::NAN);
257 assert_eq!(Float::from(-0.), Float::from(0.));
258 }
259
260 #[test]
261 fn cmp() {
262 assert_eq!(
263 Float::from(0.).partial_cmp(&Float::from(0.)),
264 Some(Ordering::Equal)
265 );
266 assert_eq!(
267 Float::INFINITY.partial_cmp(&Float::MAX),
268 Some(Ordering::Greater)
269 );
270 assert_eq!(
271 Float::NEG_INFINITY.partial_cmp(&Float::MIN),
272 Some(Ordering::Less)
273 );
274 assert_eq!(Float::NAN.partial_cmp(&Float::from(0.)), None);
275 assert_eq!(Float::NAN.partial_cmp(&Float::NAN), None);
276 assert_eq!(
277 Float::from(0.).partial_cmp(&Float::from(-0.)),
278 Some(Ordering::Equal)
279 );
280 }
281
282 #[test]
283 fn is_identical_with() {
284 assert!(Float::from(0.).is_identical_with(Float::from(0.)));
285 assert!(Float::NAN.is_identical_with(Float::NAN));
286 assert!(!Float::from(-0.).is_identical_with(Float::from(0.)));
287 }
288
289 #[test]
290 fn from_str() -> Result<(), ParseFloatError> {
291 assert_eq!(Float::from_str("NaN")?.to_string(), "NaN");
292 assert_eq!(Float::from_str("INF")?.to_string(), "INF");
293 assert_eq!(Float::from_str("+INF")?.to_string(), "INF");
294 assert_eq!(Float::from_str("-INF")?.to_string(), "-INF");
295 assert_eq!(Float::from_str("0.0E0")?.to_string(), "0");
296 assert_eq!(Float::from_str("-0.0E0")?.to_string(), "-0");
297 assert_eq!(Float::from_str("0.1e1")?.to_string(), "1");
298 assert_eq!(Float::from_str("-0.1e1")?.to_string(), "-1");
299 assert_eq!(Float::from_str("1.e1")?.to_string(), "10");
300 assert_eq!(Float::from_str("-1.e1")?.to_string(), "-10");
301 assert_eq!(Float::from_str("1")?.to_string(), "1");
302 assert_eq!(Float::from_str("-1")?.to_string(), "-1");
303 assert_eq!(Float::from_str("1.")?.to_string(), "1");
304 assert_eq!(Float::from_str("-1.")?.to_string(), "-1");
305 assert_eq!(Float::from_str(&f32::MIN.to_string())?, Float::MIN);
306 assert_eq!(Float::from_str(&f32::MAX.to_string())?, Float::MAX);
307 Ok(())
308 }
309}