1#[cfg(feature = "std")]
16#[inline]
17#[must_use]
18pub fn expm1(x: f64) -> f64 {
19 x.exp_m1()
20}
21
22#[cfg(all(not(feature = "std"), feature = "libm"))]
24#[inline]
25#[must_use]
26pub fn expm1(x: f64) -> f64 {
27 libm::expm1(x)
28}
29
30#[cfg(feature = "std")]
38#[inline]
39#[must_use]
40pub fn log1p(x: f64) -> f64 {
41 x.ln_1p()
42}
43
44#[cfg(all(not(feature = "std"), feature = "libm"))]
46#[inline]
47#[must_use]
48pub fn log1p(x: f64) -> f64 {
49 libm::log1p(x)
50}
51
52#[must_use]
63pub fn fsum(xs: &[f64]) -> f64 {
64 let mut sum = 0.0;
65 let mut correction = 0.0;
66 for &x in xs {
67 let t = sum + x;
68 if sum.abs() >= x.abs() {
69 correction += (sum - t) + x;
70 } else {
71 correction += (x - t) + sum;
72 }
73 sum = t;
74 }
75 sum + correction
76}
77
78#[inline]
88#[must_use]
89#[allow(clippy::suboptimal_flops)] pub fn isclose(a: f64, b: f64, rel_tol: f64, abs_tol: f64) -> bool {
91 (a - b).abs() <= rel_tol * b.abs().max(a.abs()) + abs_tol
92}
93
94#[must_use]
106pub fn frexp(x: f64) -> (f64, i32) {
107 if x == 0.0 || x.is_nan() || x.is_infinite() {
108 return (x, 0);
109 }
110 let bits = x.to_bits();
111 let exponent = ((bits >> 52) & 0x7FF) as i32;
112 if exponent == 0 {
113 let (m, e) = frexp(x * 4503599627370496.0); return (m, e - 52);
116 }
117 let mantissa = (bits & !(0x7FFu64 << 52)) | (1022u64 << 52);
118 (f64::from_bits(mantissa), exponent - 1022)
119}
120
121#[must_use]
131pub fn ldexp(x: f64, e: i32) -> f64 {
132 if x == 0.0 || x.is_nan() || x.is_infinite() {
133 return x;
134 }
135 let mut bits = x.to_bits();
136 let exponent = ((bits >> 52) & 0x7FF) as i32;
137 if exponent == 0 {
138 return ldexp(x * 4503599627370496.0, e - 52); }
141 let new_exp = exponent + e;
142 if new_exp <= 0 {
143 let mut r = x;
146 let mut k = e;
147 while k < 0 {
148 r *= 0.5;
149 k += 1;
150 }
151 return r;
152 }
153 if new_exp >= 0x7FF {
154 return if bits >> 63 == 1 { f64::NEG_INFINITY } else { f64::INFINITY };
155 }
156 bits = (bits & !(0x7FFu64 << 52)) | ((new_exp as u64) << 52);
157 f64::from_bits(bits)
158}
159
160#[must_use]
174pub fn modf(x: f64) -> (f64, f64) {
175 if x.is_nan() || x.is_infinite() {
176 return (x, 0.0);
177 }
178 let integral = trunc_f64(x);
179 (x - integral, integral)
180}
181
182#[inline]
187#[must_use]
188fn trunc_f64(x: f64) -> f64 {
189 if x == 0.0 || x.is_nan() || x.is_infinite() {
190 return x;
191 }
192 let bits = x.to_bits();
193 let exponent = ((bits >> 52) & 0x7FF) as i32;
194 if exponent == 0 {
195 return f64::from_bits(bits & (1u64 << 63)); }
197 let unbiased = exponent - 1023;
198 if unbiased >= 52 {
199 return x;
200 }
201 if unbiased < 0 {
202 return f64::from_bits(bits & (1u64 << 63)); }
204 let keep = 52 - unbiased; let mask = !((1u64 << keep) - 1);
206 f64::from_bits(bits & mask)
207}
208
209#[must_use]
221pub fn nextafter(x: f64, y: f64) -> f64 {
222 if x.is_nan() || y.is_nan() {
223 return f64::NAN;
224 }
225 if x == y {
226 return y;
227 }
228 if x == 0.0 {
229 let mut r = f64::from_bits(1);
230 if y < 0.0 {
231 r = -r;
232 }
233 return r;
234 }
235 let mut bits = x.to_bits();
236 if (x < y) == (x > 0.0) {
237 bits += 1;
238 } else {
239 bits -= 1;
240 }
241 f64::from_bits(bits)
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn expm1_log1p_small() {
250 assert!((expm1(1e-10) - 1e-10).abs() < 1e-20);
251 assert!((log1p(1e-10) - 1e-10).abs() < 1e-20);
252 assert!((expm1(0.0) - 0.0).abs() < 1e-20);
253 assert!((log1p(0.0) - 0.0).abs() < 1e-20);
254 }
255
256 #[test]
257 fn fsum_exactness() {
258 assert_eq!(fsum(&[1e16, 1.0, -1e16]), 1.0);
259 assert_eq!(fsum(&[0.1; 10]), 1.0);
260 }
261
262 #[test]
263 fn isclose_behavior() {
264 assert!(isclose(0.1 + 0.2, 0.3, 1e-9, 1e-12));
265 assert!(!isclose(1.0, 2.0, 1e-9, 1e-12));
266 assert!(!isclose(f64::NAN, f64::NAN, 1.0, 1.0));
267 assert!(isclose(1e-300, 1e-300, 1e-9, 1e-320));
269 }
270
271 #[test]
272 fn frexp_ldexp_roundtrip() {
273 for &x in &[0.5, 1.0, 2.0, 6.0, 123.456, 1e-300, -7.5] {
274 let (m, e) = frexp(x);
275 assert!(m.abs() >= 0.5 && m.abs() < 1.0 || x == 0.0);
276 assert!((ldexp(m, e) - x).abs() < x.abs() * 1e-12 || x == 0.0);
277 }
278 assert_eq!(frexp(0.0), (0.0, 0));
279 assert_eq!(frexp(0.75), (0.75, 0));
280 assert_eq!(frexp(6.0), (0.75, 3));
281 assert!(ldexp(1.0, 2000).is_infinite());
282 assert_eq!(ldexp(1.0, -2000), 0.0);
283 }
284
285 #[test]
286 fn modf_split() {
287 assert_eq!(modf(2.7).1, 2.0);
288 assert_eq!(modf(-2.7).1, -2.0);
289 assert_eq!(modf(0.0).1, 0.0);
290 assert_eq!(modf(f64::INFINITY).1, 0.0);
291 }
292
293 #[test]
294 fn nextafter_stepping() {
295 assert!(nextafter(1.0, 2.0) > 1.0);
296 assert!(nextafter(1.0, 0.0) < 1.0);
297 assert_eq!(nextafter(0.0, 1.0), f64::from_bits(1));
298 assert_eq!(nextafter(1.0, 1.0), 1.0);
299 assert!(nextafter(f64::MAX, f64::INFINITY).is_infinite());
300 }
301}