statcpp
C++17 Header-Only Statistics Library
Loading...
Searching...
No Matches
numerical_utils.hpp
Go to the documentation of this file.
1
6#pragma once
7
8#include <algorithm>
9#include <cmath>
10#include <iterator>
11#include <limits>
12#include <stdexcept>
13
14namespace statcpp {
15
19constexpr double epsilon = std::numeric_limits<double>::epsilon();
20
24constexpr double default_rel_tol = 1e-9;
25
29constexpr double default_abs_tol = 1e-12;
30
43inline bool approx_equal(double a, double b,
44 double rel_tol = default_rel_tol,
45 double abs_tol = default_abs_tol)
46{
47 // Handle exact equality (including infinities)
48 if (a == b) {
49 return true;
50 }
51
52 // Handle NaN
53 if (std::isnan(a) || std::isnan(b)) {
54 return false;
55 }
56
57 // Compute absolute difference
58 double diff = std::abs(a - b);
59
60 // Check absolute tolerance
61 if (diff <= abs_tol) {
62 return true;
63 }
64
65 // Check relative tolerance
66 double max_abs = std::max(std::abs(a), std::abs(b));
67 return diff <= rel_tol * max_abs;
68}
69
77inline bool is_zero(double x, double tol = default_abs_tol)
78{
79 return std::abs(x) <= tol;
80}
81
88inline bool is_finite(double x)
89{
90 return std::isfinite(x);
91}
92
101template <typename Iterator>
102bool all_finite(Iterator first, Iterator last)
103{
104 for (auto it = first; it != last; ++it) {
105 if (!std::isfinite(static_cast<double>(*it))) {
106 return false;
107 }
108 }
109 return true;
110}
111
122inline bool has_converged_abs(double x_new, double x_old,
123 double tol = 1e-6)
124{
125 return std::abs(x_new - x_old) <= tol;
126}
127
138inline bool has_converged_rel(double x_new, double x_old,
139 double tol = 1e-6)
140{
141 double denominator = std::max(std::abs(x_old), epsilon);
142 return std::abs(x_new - x_old) / denominator <= tol;
143}
144
156inline bool has_converged(double x_new, double x_old,
157 double abs_tol = 1e-8,
158 double rel_tol = 1e-6)
159{
160 double threshold = abs_tol + rel_tol * std::abs(x_old);
161 return std::abs(x_new - x_old) <= threshold;
162}
163
172inline double log1p_safe(double x)
173{
174 return std::log1p(x);
175}
176
185inline double expm1_safe(double x)
186{
187 return std::expm1(x);
188}
189
199inline double clamp(double x, double min_val, double max_val)
200{
201 if (min_val > max_val) {
202 throw std::invalid_argument("statcpp::clamp: min_val must be <= max_val");
203 }
204 return std::max(min_val, std::min(x, max_val));
205}
206
215inline bool in_range(double x, double min_val, double max_val)
216{
217 return x >= min_val && x <= max_val;
218}
219
229inline double relative_error(double x, double x_ref)
230{
231 double denominator = std::max(std::abs(x_ref), epsilon);
232 return std::abs(x - x_ref) / denominator;
233}
234
243inline double safe_divide(double numerator, double denominator,
244 double default_value = std::numeric_limits<double>::quiet_NaN())
245{
246 if (is_zero(denominator)) {
247 return default_value;
248 }
249 return numerator / denominator;
250}
251
263template <typename Iterator>
264double kahan_sum(Iterator first, Iterator last)
265{
266 double sum = 0.0;
267 double compensation = 0.0; // Running compensation for lost low-order bits
268
269 for (auto it = first; it != last; ++it) {
270 double value = static_cast<double>(*it);
271 double y = value - compensation;
272 double t = sum + y;
273 compensation = (t - sum) - y;
274 sum = t;
275 }
276
277 return sum;
278}
279
283template <typename Iterator, typename Proj>
284double kahan_sum(Iterator first, Iterator last, Proj proj)
285{
286 double sum = 0.0;
287 double compensation = 0.0;
288
289 for (auto it = first; it != last; ++it) {
290 double value = static_cast<double>(proj(*it));
291 double y = value - compensation;
292 double t = sum + y;
293 compensation = (t - sum) - y;
294 sum = t;
295 }
296
297 return sum;
298}
299
313template <typename Iterator1, typename Iterator2>
314bool approx_equal_range(Iterator1 first1, Iterator1 last1,
315 Iterator2 first2, Iterator2 last2,
316 double rel_tol = default_rel_tol,
317 double abs_tol = default_abs_tol)
318{
319 auto n1 = std::distance(first1, last1);
320 auto n2 = std::distance(first2, last2);
321
322 if (n1 != n2) {
323 return false;
324 }
325
326 auto it1 = first1;
327 auto it2 = first2;
328
329 while (it1 != last1) {
330 double v1 = static_cast<double>(*it1);
331 double v2 = static_cast<double>(*it2);
332
333 if (!approx_equal(v1, v2, rel_tol, abs_tol)) {
334 return false;
335 }
336
337 ++it1;
338 ++it2;
339 }
340
341 return true;
342}
343
344} // namespace statcpp
constexpr double default_abs_tol
Default absolute tolerance for floating-point comparisons.
bool has_converged_abs(double x_new, double x_old, double tol=1e-6)
Check for convergence based on absolute change.
bool approx_equal_range(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, double rel_tol=default_rel_tol, double abs_tol=default_abs_tol)
Check if two ranges are approximately equal element-wise.
double safe_divide(double numerator, double denominator, double default_value=std::numeric_limits< double >::quiet_NaN())
Safe division with check for division by zero.
double relative_error(double x, double x_ref)
Compute the relative error between two values.
double kahan_sum(Iterator first, Iterator last)
Compute sum with Kahan summation algorithm.
auto sum(Iterator first, Iterator last)
Sum.
double log1p_safe(double x)
Safely compute log(1 + x) for small x.
constexpr double epsilon
Machine epsilon for double precision.
bool all_finite(Iterator first, Iterator last)
Check if all values in a range are finite.
bool in_range(double x, double min_val, double max_val)
Check if a value is in range [min_val, max_val].
bool has_converged_rel(double x_new, double x_old, double tol=1e-6)
Check for convergence based on relative change.
bool is_finite(double x)
Check if a value is finite (not infinity or NaN)
bool has_converged(double x_new, double x_old, double abs_tol=1e-8, double rel_tol=1e-6)
Check for convergence using combined absolute and relative criteria.
std::vector< double > diff(Iterator first, Iterator last, std::size_t order=1)
Difference series (first-order or d-th order differencing)
double clamp(double x, double min_val, double max_val)
Clamp a value to a range [min_val, max_val].
bool approx_equal(double a, double b, double rel_tol=default_rel_tol, double abs_tol=default_abs_tol)
Check if two floating-point numbers are approximately equal.
bool is_zero(double x, double tol=default_abs_tol)
Check if a value is close to zero.
double expm1_safe(double x)
Safely compute exp(x) - 1 for small x.
constexpr double default_rel_tol
Default relative tolerance for floating-point comparisons.