1pub trait Float: Sized {
9 #[must_use]
11 fn round(self) -> Self;
12 #[must_use]
14 fn floor(self) -> Self;
15 #[must_use]
17 fn sin(self) -> Self;
18 #[must_use]
20 fn cos(self) -> Self;
21 #[must_use]
23 fn hypot(self, other: Self) -> Self;
24 #[must_use]
26 fn rem_euclid(self, rhs: Self) -> Self;
27}
28
29impl Float for f32 {
30 fn round(self) -> Self {
31 libm::roundf(self)
32 }
33 fn floor(self) -> Self {
34 libm::floorf(self)
35 }
36 fn sin(self) -> Self {
37 libm::sinf(self)
38 }
39 fn cos(self) -> Self {
40 libm::cosf(self)
41 }
42 fn hypot(self, other: Self) -> Self {
43 libm::hypotf(self, other)
44 }
45 fn rem_euclid(self, rhs: Self) -> Self {
46 let r = self % rhs;
48 if r < 0.0 { r + rhs.abs() } else { r }
49 }
50}
51
52pub trait Round {
54 #[must_use]
56 fn round(self) -> Self;
57}
58
59impl Round for f64 {
60 fn round(self) -> Self {
61 libm::round(self)
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::{Float, Round};
68 use proptest::prelude::*;
69
70 fn same(a: f32, b: f32) -> bool {
72 a.to_bits() == b.to_bits()
73 }
74
75 fn close(a: f32, b: f32) -> bool {
78 (a - b).abs() <= 1e-6 * b.abs().max(1.0)
79 }
80
81 #[test]
82 fn rounding_matches_std_at_halves_and_signs() {
83 for v in [-2.5_f32, -1.5, -0.5, -0.4, 0.0, 0.4, 0.5, 1.5, 2.5, 7.99] {
84 assert!(same(Float::round(v), v.round()), "round {v}");
85 assert!(same(Float::floor(v), v.floor()), "floor {v}");
86 let w = f64::from(v);
87 assert_eq!(Round::round(w).to_bits(), w.round().to_bits(), "round {v}");
88 }
89 }
90
91 #[test]
92 fn rem_euclid_matches_std_at_signs_and_zero() {
93 for (v, w) in [
94 (-1.0_f32, 4.0),
95 (-1.0, -4.0),
96 (5.0, 4.0),
97 (-4.0, 4.0),
98 (0.5, 0.25),
99 ] {
100 assert!(same(Float::rem_euclid(v, w), v.rem_euclid(w)), "{v} % {w}");
101 }
102 }
103
104 proptest! {
105 #[test]
106 fn matches_std(v in -1000.0_f32..1000.0, w in -1000.0_f32..1000.0) {
107 prop_assert!(same(Float::round(v), v.round()));
108 prop_assert!(same(Float::floor(v), v.floor()));
109 let x = f64::from(v) * 3.7;
110 prop_assert_eq!(Round::round(x).to_bits(), x.round().to_bits());
111 prop_assert!(close(Float::sin(v / 100.0), (v / 100.0).sin()));
112 prop_assert!(close(Float::cos(v / 100.0), (v / 100.0).cos()));
113 prop_assert!(close(Float::hypot(v, w), v.hypot(w)));
114 if w != 0.0 {
115 prop_assert!(same(Float::rem_euclid(v, w), v.rem_euclid(w)));
116 }
117 }
118 }
119}