File indexing completed on 2024-05-12 03:47:52

0001 /*
0002     File                 : nsl_math.c
0003     Project              : LabPlot
0004     Description          : NSL math functions
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2018-2024 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "nsl_math.h"
0011 #include <gsl/gsl_math.h>
0012 #include <stdio.h>
0013 
0014 bool nsl_math_approximately_equal(double a, double b) {
0015     return nsl_math_approximately_equal_eps(a, b, 1.e-7);
0016 }
0017 
0018 bool nsl_math_approximately_equal_eps(double a, double b, double epsilon) {
0019     return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
0020 }
0021 
0022 bool nsl_math_essentially_equal(double a, double b) {
0023     return nsl_math_essentially_equal_eps(a, b, 1.e-7);
0024 }
0025 
0026 bool nsl_math_essentially_equal_eps(double a, double b, double epsilon) {
0027     return fabs(a - b) <= ((fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
0028 }
0029 
0030 bool nsl_math_definitely_greater_than(double a, double b) {
0031     return nsl_math_definitely_greater_than_eps(a, b, 1.e-7);
0032 }
0033 
0034 bool nsl_math_definitely_greater_than_eps(double a, double b, double epsilon) {
0035     return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
0036 }
0037 
0038 bool nsl_math_definitely_less_than(double a, double b) {
0039     return nsl_math_definitely_less_than_eps(a, b, 1.e-7);
0040 }
0041 
0042 bool nsl_math_definitely_less_than_eps(double a, double b, double epsilon) {
0043     return (b - a) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
0044 }
0045 
0046 double nsl_math_frexp10(double x, int* e) {
0047     int expo = 0;
0048     if (x != 0)
0049         expo = floor(log10(fabs(x)));
0050 
0051     if (e != NULL)
0052         *e = expo;
0053 
0054     return x / gsl_pow_int(10., expo);
0055 }
0056 
0057 /* rounding methods */
0058 
0059 int nsl_math_decimal_places(double value) {
0060     return -(int)floor(log10(fabs(value)));
0061 }
0062 
0063 int nsl_math_rounded_decimals(double value) {
0064     int places = nsl_math_decimal_places(value);
0065 
0066     // printf("places = %d, rv = %g\n", places, round(fabs(value) * pow(10, places)));
0067     if (round(fabs(value) * gsl_pow_int(10., places)) >= 5.)
0068         places--;
0069 
0070     return places;
0071 }
0072 
0073 int nsl_math_rounded_decimals_max(double value, int max) {
0074     return GSL_MIN_INT(max, nsl_math_rounded_decimals(value));
0075 }
0076 
0077 double nsl_math_round_places(double value, int n) {
0078     return nsl_math_places(value, n, 0);
0079 }
0080 double nsl_math_floor_places(double value, int n) {
0081     return nsl_math_places(value, n, 1);
0082 }
0083 double nsl_math_ceil_places(double value, int n) {
0084     return nsl_math_places(value, n, 2);
0085 }
0086 double nsl_math_trunc_places(double value, int n) {
0087     return nsl_math_places(value, n, 3);
0088 }
0089 
0090 double nsl_math_places(double value, int n, int method) {
0091     // no need to round
0092     if (value == 0. || fabs(value) > 1.e16 || fabs(value) < 1.e-16 || isnan(value) || isinf(value)) {
0093         /*      printf("nsl_math_places(): not changed : %.19g\n", value); */
0094         return value;
0095     }
0096 
0097     double scale = gsl_pow_int(10., n);
0098     double scaled_value = value * scale;
0099     if (fabs(scaled_value) > 1.e16)
0100         return value;
0101     if (fabs(scaled_value) < .5)
0102         return 0.;
0103 
0104     double eps = 1.e-15;
0105     /*
0106         printf("nsl_math_places(): value = %g, n = %d, DBL_EPSILON = %.19g, scale = %.19g, scaled_value = %.19g, round(scaled_value) = %.19g
0107        round(scaled_value)/scale = %.19g\n", value, n, DBL_EPSILON, scale, scaled_value, round(scaled_value), round(scaled_value)/scale);
0108     */
0109     switch (method) {
0110     case 0:
0111         return round(scaled_value) / scale;
0112         break;
0113     case 1:
0114         return floor(scaled_value + eps) / scale;
0115         break;
0116     case 2:
0117         return ceil(scaled_value - eps) / scale;
0118         break;
0119     case 3:
0120         return trunc(scaled_value) / scale;
0121         break;
0122     default:
0123         printf("ERROR: unknown rounding method %d\n", method);
0124         return value;
0125     }
0126 }
0127 
0128 double nsl_math_round_precision(double value, int p) {
0129     return nsl_math_round_basex(value, p, 10.);
0130 }
0131 
0132 double nsl_math_round_basex(double value, int p, double base) {
0133     /*  printf("nsl_math_round_basex(%g, %d, %g)\n", value, p, base); */
0134 
0135     // no need to round
0136     if (value == 0. || p > 16 || isnan(value) || isinf(value))
0137         return value;
0138 
0139     int e = 0;
0140     while (fabs(value) > base) {
0141         value /= base;
0142         e++;
0143     }
0144     while (fabs(value) < 1.) {
0145         value *= base;
0146         e--;
0147     }
0148     double power_of_x = gsl_pow_int(base, e);
0149 
0150     if (p < 0)
0151         return power_of_x;
0152 
0153     double scale = gsl_pow_uint(base, p);
0154     double scaled_value = value * scale;
0155     /*
0156         printf("nsl_math_round_basex(): scale = %g, scaled_value = %g, e = %d, return: %g\n", scale, scaled_value, e, round(scaled_value)/scale *
0157        gsl_pow_int(10., e));
0158     */
0159 
0160     return round(scaled_value) / scale * power_of_x;
0161 }
0162 
0163 double nsl_math_round_multiple(double value, double m) {
0164     return nsl_math_multiple(value, m, Round);
0165 }
0166 double nsl_math_floor_multiple(double value, double m) {
0167     return nsl_math_multiple(value, m, Floor);
0168 }
0169 double nsl_math_ceil_multiple(double value, double m) {
0170     return nsl_math_multiple(value, m, Ceil);
0171 }
0172 double nsl_math_trunc_multiple(double value, double m) {
0173     return nsl_math_multiple(value, m, Trunc);
0174 }
0175 
0176 double nsl_math_multiple(double value, double multiple, round_method method) {
0177     // no need to round
0178     if (value == 0. || multiple == 0. || isnan(value) || isnan(multiple) || isinf(value) || isinf(multiple)) {
0179         /*      printf("nsl_math_multiple(): not changed : %.19g\n", value); */
0180         return value;
0181     }
0182 
0183     switch (method) {
0184     case Round:
0185         return multiple * round(value / multiple);
0186     case Floor:
0187         return multiple * floor(value / multiple);
0188     case Ceil:
0189         return multiple * ceil(value / multiple);
0190     case Trunc:
0191         return multiple * trunc(value / multiple);
0192     }
0193 
0194     return value;
0195 }