1use crate::RoundingMode;
2
3#[cfg(not(feature = "std"))]
4use libm;
5
6#[cfg(feature = "rust_decimal")]
7use rust_decimal::prelude::FromPrimitive;
8#[cfg(feature = "rust_decimal")]
9use rust_decimal::{Decimal, MathematicalOps};
10
11pub trait FloatLike:
15 Copy
16 + core::fmt::Display
17 + core::fmt::Debug
18 + PartialOrd
19 + PartialEq
20 + core::ops::Add<Output = Self>
21 + core::ops::Sub<Output = Self>
22 + core::ops::Mul<Output = Self>
23 + core::ops::Div<Output = Self>
24 + core::ops::Neg<Output = Self>
25 + core::iter::Sum<Self>
26 + core::iter::Product<Self>
27 + core::ops::SubAssign
28 + core::ops::AddAssign
29 + core::ops::MulAssign
30{
31 const MAX: Self;
32 fn zero() -> Self;
33 fn one() -> Self;
34 fn powf(self, n: Self) -> Self;
35 fn two() -> Self {
36 Self::one() + Self::one()
37 }
38 fn abs(&self) -> Self;
39 fn is_zero(&self) -> bool {
40 *self == Self::zero()
41 }
42 fn min(self, other: Self) -> Self;
43 fn max(self, other: Self) -> Self;
44 fn from_u16(n: u16) -> Self;
45 fn from_usize(n: usize) -> Self;
46 fn from_i32(n: i32) -> Self;
47 fn from_f32(n: f32) -> Self;
48
49 fn round_with_mode(&self, dp: u32, mode: RoundingMode, epsilon: Self) -> Self;
50}
51
52#[crabtime::function]
54fn gen_floats(ftype: Vec<String>) {
55 for t in ftype.iter() {
56 let append = if *t == "f32" { "f" } else { "" };
57 crabtime::output! {
58 impl FloatLike for {{t}} {
59 const MAX: Self = {{t}}::MAX;
60 fn zero() -> Self {
61 0.0
62 }
63 fn one() -> Self {
64 1.0
65 }
66 fn powf(self, n: Self) -> Self {
67 #[cfg(feature = "std")]
68 { {{t}}::powf(self, n) }
69 #[cfg(not(feature = "std"))]
70 { libm::pow{{append}}(self, n) }
71 }
72 fn abs(&self) -> Self {
73 #[cfg(feature = "std")]
74 { {{t}}::abs(*self) }
75 #[cfg(not(feature = "std"))]
76 { libm::fabs{{append}}(*self) }
77 }
78 fn min(self, other: Self) -> Self {
79 {{t}}::min(self, other)
80 }
81 fn max(self, other: Self) -> Self {
82 {{t}}::max(self, other)
83 }
84 fn from_u16(n: u16) -> Self {
85 n as {{t}}
86 }
87 fn from_usize(n: usize) -> Self {
88 n as {{t}}
89 }
90 fn from_i32(n: i32) -> Self {
91 n as {{t}}
92 }
93 fn from_f32(n: f32) -> Self {
94 n as {{t}}
95 }
96 fn round_with_mode(&self, dp: u32, mode: RoundingMode, epsilon: Self) -> Self {
97 let factor = FloatLike::powf(10.0{{t}}, dp as {{t}});
98 let shifted = *self * factor;
99
100 #[cfg(feature = "std")]
101 let floor = shifted.floor();
102 #[cfg(not(feature = "std"))]
103 let floor = libm::floor{{append}}(shifted);
104
105 #[cfg(feature = "std")]
107 let shifted_rd = shifted.round();
108 #[cfg(not(feature = "std"))]
109 let shifted_rd = libm::round{{append}}(shifted);
110
111 let diff_floor = shifted - floor;
112
113 #[cfg(feature = "std")]
114 let ceil = shifted.ceil();
115 #[cfg(not(feature = "std"))]
116 let ceil = libm::ceil{{append}}(shifted);
117
118 let rounded = match mode {
119 RoundingMode::HalfToEven =>
120 {
122 if (diff_floor.abs() - 0.5).abs() > epsilon {
123 shifted_rd
124 } else {
125 if floor as i64 % 2 == 0 {
127 floor
128 } else {
129 ceil
130 }
131 }
132 }
133 RoundingMode::HalfTowardZero => {
134 if (diff_floor.abs() - 0.5).abs() > epsilon {
136 shifted_rd
137 } else {
138 if shifted > 0.0 {
140 floor
141 } else {
142 ceil
143 }
144 }
145 }
146 RoundingMode::HalfAwayFromZero => {
147 if (diff_floor.abs() - 0.5).abs() > epsilon {
149 shifted_rd
150 } else {
151 if shifted > 0.0 {
153 ceil
154 } else {
155 floor
156 }
157 }
158 }
159 RoundingMode::TowardZero => {
160 if shifted > 0.0 {
162 floor
163 } else {
164 ceil
165 }
166 }
167 RoundingMode::AwayFromZero => {
168 if shifted > 0.0 {
170 ceil
171 } else {
172 floor
173 }
174 }
175 RoundingMode::ToNegativeInfinity => floor,
177 RoundingMode::ToInfinity => ceil
179 };
180 rounded / factor
181 }
182 }
183 }
184 }
185}
186gen_floats!(["f32", "f64"]);
187
188#[cfg(feature = "rust_decimal")]
189impl FloatLike for Decimal {
190 const MAX: Self = Decimal::MAX;
191 fn zero() -> Self {
192 Decimal::ZERO
193 }
194
195 fn one() -> Self {
196 Decimal::ONE
197 }
198 fn powf(self, n: Self) -> Self {
199 self.powd(n)
200 }
201 fn abs(&self) -> Self {
202 Decimal::abs(self)
203 }
204 fn round_with_mode(&self, dp: u32, mode: RoundingMode, epsilon: Self) -> Self {
205 let rounding_mode = match mode {
206 RoundingMode::HalfToEven => rust_decimal::RoundingStrategy::MidpointNearestEven,
207 RoundingMode::HalfAwayFromZero => rust_decimal::RoundingStrategy::MidpointAwayFromZero,
208 RoundingMode::HalfTowardZero => rust_decimal::RoundingStrategy::MidpointTowardZero,
209 RoundingMode::TowardZero => rust_decimal::RoundingStrategy::ToZero,
210 RoundingMode::AwayFromZero => rust_decimal::RoundingStrategy::AwayFromZero,
211 RoundingMode::ToNegativeInfinity => rust_decimal::RoundingStrategy::ToNegativeInfinity,
212 RoundingMode::ToInfinity => rust_decimal::RoundingStrategy::ToPositiveInfinity,
213 };
214 self.round_dp_with_strategy(dp, rounding_mode)
215 }
216 fn min(self, other: Self) -> Self {
217 Decimal::min(self, other)
218 }
219 fn max(self, other: Self) -> Self {
220 Decimal::max(self, other)
221 }
222 fn from_usize(n: usize) -> Self {
223 Decimal::from(n)
224 }
225 fn from_u16(n: u16) -> Self {
226 Decimal::from(n)
227 }
228 fn from_i32(n: i32) -> Self {
229 Decimal::from(n)
230 }
231 fn from_f32(n: f32) -> Self {
233 <Decimal as FromPrimitive>::from_f32(n).expect("Failed to convert f32 to Decimal")
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[cfg(not(feature = "std"))]
242 extern crate std;
243 #[cfg(not(feature = "std"))]
244 use std::assert_eq;
245
246 #[test]
247 fn test_floatlike_rounding() {
248 let value: f32 = 2.5;
249 assert_eq!(value.round_with_mode(0, RoundingMode::HalfToEven, 1e-5), 2.0);
250 assert_eq!(value.round_with_mode(0, RoundingMode::HalfAwayFromZero, 1e-5), 3.0);
251 assert_eq!(value.round_with_mode(0, RoundingMode::HalfTowardZero, 1e-5), 2.0);
252 assert_eq!(value.round_with_mode(0, RoundingMode::TowardZero, 1e-5), 2.0);
253 assert_eq!(value.round_with_mode(0, RoundingMode::AwayFromZero, 1e-5), 3.0);
254 assert_eq!(value.round_with_mode(0, RoundingMode::ToNegativeInfinity, 1e-5), 2.0);
255 assert_eq!(value.round_with_mode(0, RoundingMode::ToInfinity, 1e-5), 3.0);
256
257 let value: f32 = -2.5;
258 assert_eq!(value.round_with_mode(0, RoundingMode::HalfToEven, 1e-5), -2.0);
259 assert_eq!(value.round_with_mode(0, RoundingMode::HalfAwayFromZero, 1e-5), -3.0);
260 assert_eq!(value.round_with_mode(0, RoundingMode::HalfTowardZero, 1e-5), -2.0);
261 assert_eq!(value.round_with_mode(0, RoundingMode::TowardZero, 1e-5), -2.0);
262 assert_eq!(value.round_with_mode(0, RoundingMode::AwayFromZero, 1e-5), -3.0);
263 assert_eq!(value.round_with_mode(0, RoundingMode::ToNegativeInfinity, 1e-5), -3.0);
264 assert_eq!(value.round_with_mode(0, RoundingMode::ToInfinity, 1e-5), -2.0);
265
266 let value: f32 = 3.005;
267 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), 3.00);
268 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), 3.01);
269 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), 3.00);
270 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), 3.00);
271 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), 3.01);
272 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), 3.00);
273 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), 3.01);
274
275 let value: f64 = -3.005;
276 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), -3.00);
277 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), -3.01);
278 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), -3.00);
279 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), -3.00);
280 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), -3.01);
281 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), -3.01);
282 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), -3.00);
283
284 let value: f64 = 0.333;
285 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), 0.33);
286 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), 0.33);
287 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), 0.33);
288 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), 0.33);
289 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), 0.34);
290 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), 0.33);
291 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), 0.34);
292
293 let value: f64 = -0.333;
294 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), -0.33);
295 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), -0.33);
296 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), -0.33);
297 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), -0.33);
298 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), -0.34);
299 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), -0.34);
300 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), -0.33);
301
302 let value: f64 = 0.555;
303 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), 0.56);
304 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), 0.56);
305 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), 0.55);
306 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), 0.55);
307 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), 0.56);
308 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), 0.55);
309 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), 0.56);
310
311 let value: f64 = 3.00;
312 assert_eq!(value.round_with_mode(2, RoundingMode::HalfToEven, 1e-5), 3.00);
313 assert_eq!(value.round_with_mode(2, RoundingMode::HalfAwayFromZero, 1e-5), 3.00);
314 assert_eq!(value.round_with_mode(2, RoundingMode::HalfTowardZero, 1e-5), 3.00);
315 assert_eq!(value.round_with_mode(2, RoundingMode::TowardZero, 1e-5), 3.00);
316 assert_eq!(value.round_with_mode(2, RoundingMode::AwayFromZero, 1e-5), 3.00);
317 assert_eq!(value.round_with_mode(2, RoundingMode::ToNegativeInfinity, 1e-5), 3.00);
318 assert_eq!(value.round_with_mode(2, RoundingMode::ToInfinity, 1e-5), 3.00);
319 }
320}