/********************************************************************************************************* * ------------------------------------------------------------------------------------------------------ * file description * ------------------------------------------------------------------------------------------------------ * \file flmath.c * \unit flmath * \brief This is a simple large float number math calculate module for C language * \author Lamdonn * \version v1.0.0 * \license GPL-2.0 * \copyright Copyright (C) 2023 Lamdonn. ********************************************************************************************************/ #include "flmath.h" // Number of terms in the Taylor series for trigonometric functions static const int trig_terms = 15 * sizeof(floatl) / 8; // Number of terms in the Taylor series for the exponential function static const int exp_terms = 15 * sizeof(floatl) / 8; // Number of terms in the Taylor series for the natural logarithm function static const int ln_terms = 15 * sizeof(floatl) / 8; /** * \brief Calculates the factorial of an integer. * \param n: The integer for which the factorial is to be calculated. * \return A 'floatl' number representing the factorial of 'n'. */ static floatl floatl_int_fact(int n) { floatl result = FLOATL_CONST_1; for (int i = 1; i <= n; i++) { result = floatl_mul(result, floatl(i)); } return result; } /** * \brief Calculates the power of a 'floatl' number with an integer exponent. * \param base: The base 'floatl' number. * \param exponent: The integer exponent. * \return A 'floatl' number representing 'base' raised to the power of 'exponent'. */ static floatl floatl_int_power(floatl base, int exponent) { floatl result = FLOATL_CONST_1; for (int i = 0; i < exponent; i++) { result = floatl_mul(result, base); } return result; } /** * \brief Calculates the sine of a 'floatl' number using the Taylor series expansion. * \param x: The 'floatl' number for which the sine is to be calculated. * \return A 'floatl' number representing the sine of 'x'. */ floatl floatl_sin(floatl x) { floatl result = FLOATL_CONST_0; // Normalize x to the range [-π, π] while (floatl_gt(x, FLOATL_PI)) { x = floatl_sub(x, FLOATL_PI); x = floatl_sub(x, FLOATL_PI); } while (floatl_lt(x, floatl_neg(FLOATL_PI))) { x = floatl_add(x, FLOATL_PI); x = floatl_add(x, FLOATL_PI); } for (int n = 0; n < trig_terms; n++) { floatl term = floatl_int_power(floatl_neg(FLOATL_CONST_1), n); term = floatl_mul(term, floatl_int_power(x, 2 * n + 1)); term = floatl_div(term, floatl_int_fact(2 * n + 1)); result = floatl_add(result, term); } return result; } /** * \brief Calculates the cosine of a 'floatl' number using the sine function. * \param x: The 'floatl' number for which the cosine is to be calculated. * \return A 'floatl' number representing the cosine of 'x'. */ floatl floatl_cos(floatl x) { x = floatl_add(x, floatl_mul(floatl(0.5), FLOATL_PI)); return floatl_sin(x); } /** * \brief Calculates the secant of a 'floatl' number. * \param x: The 'floatl' number for which the secant is to be calculated. * \return A 'floatl' number representing the secant of 'x'. */ floatl floatl_sec(floatl x) { return floatl_div(FLOATL_CONST_1, floatl_cos(x)); } /** * \brief Calculates the cosecant of a 'floatl' number. * \param x: The 'floatl' number for which the cosecant is to be calculated. * \return A 'floatl' number representing the cosecant of 'x'. */ floatl floatl_csc(floatl x) { return floatl_div(FLOATL_CONST_1, floatl_sin(x)); } /** * \brief Calculates the tangent of a 'floatl' number. * \param x: The 'floatl' number for which the tangent is to be calculated. * \return A 'floatl' number representing the tangent of 'x'. Returns infinity or negative infinity * if the cosine of 'x' is zero. */ floatl floatl_tan(floatl x) { floatl s = floatl_sin(x); floatl c = floatl_cos(x); if (floatl_eq(c, FLOATL_CONST_0)) { return (floatl_gt(s, FLOATL_CONST_0)) ? FLOATL_INF : floatl_neg(FLOATL_INF); } return floatl_div(s, c); } /** * \brief Calculates the cotangent of a 'floatl' number. * \param x: The 'floatl' number for which the cotangent is to be calculated. * \return A 'floatl' number representing the cotangent of 'x'. */ floatl floatl_cot(floatl x) { x = floatl_sub(floatl_mul(floatl(0.5), FLOATL_PI), x); return floatl_tan(x); } /** * \brief Calculates the exponential function of a 'floatl' number using the Taylor series expansion. * \param x: The 'floatl' number for which the exponential is to be calculated. * \return A 'floatl' number representing e raised to the power of 'x'. */ floatl floatl_exp(floatl x) { floatl result = FLOATL_CONST_0; for (int n = 0; n < exp_terms; n++) { floatl term = floatl_int_power(x, n); term = floatl_div(term, floatl_int_fact(n)); result = floatl_add(result, term); } return result; } /** * \brief Calculates the natural logarithm of a 'floatl' number using the Taylor series expansion. * \param x: The 'floatl' number for which the natural logarithm is to be calculated. * \return A 'floatl' number representing the natural logarithm of 'x'. Returns -1 if 'x' is less than or equal to 0. */ floatl floatl_ln(floatl x) { if (floatl_le(x, FLOATL_CONST_0)) return floatl_neg(FLOATL_CONST_1); floatl result = FLOATL_CONST_0; // Transform x to the range (0, 2] floatl y = floatl_sub(x, FLOATL_CONST_1); y = floatl_div(y, floatl_add(x, FLOATL_CONST_1)); for (int n = 0; n < ln_terms; n++) { floatl term = floatl_int_power(y, 2 * n + 1); term = floatl_div(term, floatl(2 * n + 1)); result = floatl_add(result, term); } result = floatl_mul(result, floatl(2.0)); return result; } /** * \brief Calculates the logarithm of a 'floatl' number with a given base. * \param x: The base 'floatl' number. * \param y: The 'floatl' number for which the logarithm is to be calculated. * \return A 'floatl' number representing the logarithm of 'y' with base 'x'. Returns negative infinity * if 'x' is zero or 'y' is less than or equal to 0. */ floatl floatl_log(floatl x, floatl y) { if ((floatl_eq(x, FLOATL_CONST_0)) || (floatl_le(y, FLOATL_CONST_0))) return floatl_neg(FLOATL_INF); return floatl_div(floatl_ln(y), floatl_ln(x)); } /** * \brief Calculates the logarithm of a 'floatl' number with base 2. * \param x: The 'floatl' number for which the logarithm is to be calculated. * \return A 'floatl' number representing the logarithm of 'x' with base 2. Returns negative infinity * if 'x' is zero. */ floatl floatl_log2(floatl x) { if (floatl_eq(x, FLOATL_CONST_0)) return floatl_neg(FLOATL_INF); return floatl_div(floatl_ln(x), floatl_ln(floatl(2.0))); } /** * \brief Calculates the logarithm of a 'floatl' number with base 10. * \param x: The 'floatl' number for which the logarithm is to be calculated. * \return A 'floatl' number representing the logarithm of 'x' with base 10. Returns negative infinity * if 'x' is zero. */ floatl floatl_log10(floatl x) { if (floatl_eq(x, FLOATL_CONST_0)) return floatl_neg(FLOATL_INF); return floatl_div(floatl_ln(x), floatl_ln(floatl(10.0))); } /** * \brief Calculates the sigmoid function of a 'floatl' number. * \param x: The 'floatl' number for which the sigmoid is to be calculated. * \return A 'floatl' number representing the sigmoid of 'x'. */ floatl floatl_sigmoid(floatl x) { return floatl_div(FLOATL_CONST_1, floatl_add(FLOATL_CONST_1, floatl_exp(floatl_neg(x)))); } /** * \brief Calculates the derivative of the sigmoid function of a 'floatl' number. * \param x: The 'floatl' number for which the derivative of the sigmoid is to be calculated. * \return A 'floatl' number representing the derivative of the sigmoid of 'x'. */ floatl floatl_dsigmoid(floatl x) { floatl s = floatl_sigmoid(x); return floatl_mul(s, floatl_sub(FLOATL_CONST_1, s)); } /** * \brief Calculates the hyperbolic sine of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic sine is to be calculated. * \return A 'floatl' number representing the hyperbolic sine of 'x'. */ floatl floatl_sinh(floatl x) { floatl a = floatl_exp(x); return floatl_div(floatl_sub(a, floatl_div(FLOATL_CONST_1, a)), floatl(2.0)); } /** * \brief Calculates the hyperbolic cosine of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic cosine is to be calculated. * \return A 'floatl' number representing the hyperbolic cosine of 'x'. */ floatl floatl_cosh(floatl x) { floatl a = floatl_exp(x); return floatl_div(floatl_add(a, floatl_div(FLOATL_CONST_1, a)), floatl(2.0)); } /** * \brief Calculates the hyperbolic tangent of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic tangent is to be calculated. * \return A 'floatl' number representing the hyperbolic tangent of 'x'. */ floatl floatl_tanh(floatl x) { floatl a = floatl_exp(x); floatl b = floatl_div(FLOATL_CONST_1, a); return floatl_div(floatl_sub(a, b), floatl_add(a, b)); } /** * \brief Calculates the hyperbolic cotangent of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic cotangent is to be calculated. * \return A 'floatl' number representing the hyperbolic cotangent of 'x'. */ floatl floatl_coth(floatl x) { floatl a = floatl_exp(x); floatl b = floatl_div(FLOATL_CONST_1, a); return floatl_div(floatl_add(a, b), floatl_sub(a, b)); } /** * \brief Calculates the hyperbolic secant of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic secant is to be calculated. * \return A 'floatl' number representing the hyperbolic secant of 'x'. */ floatl floatl_sech(floatl x) { floatl a = floatl_exp(x); return floatl_div(floatl(2.0), floatl_add(a, floatl_div(FLOATL_CONST_1, a))); } /** * \brief Calculates the hyperbolic cosecant of a 'floatl' number. * \param x: The 'floatl' number for which the hyperbolic cosecant is to be calculated. * \return A 'floatl' number representing the hyperbolic cosecant of 'x'. */ floatl floatl_csch(floatl x) { floatl a = floatl_exp(x); return floatl_div(floatl(2.0), floatl_sub(a, floatl_div(FLOATL_CONST_1, a))); } /** * \brief Calculates the square root of a 'floatl' number using the Newton - Raphson method. * \param x: The 'floatl' number for which the square root is to be calculated. * \return A 'floatl' number representing the square root of 'x'. Returns -1 if 'x' is less than 0. */ floatl floatl_sqrt(floatl x) { if (floatl_lt(x, FLOATL_CONST_0)) return floatl_neg(FLOATL_CONST_1); floatl a = x; // Initial guess floatl epsilon = floatl(1e-7); // Precision while (floatl_gt(floatl_sub(floatl_mul(a, a), x), epsilon)) { a = floatl_add(a, floatl_div(x, a)); a = floatl_div(a, floatl(2.0)); } return a; } /** * \brief Calculates the power of a 'floatl' number with another 'floatl' exponent. * \param x: The base 'floatl' number. * \param y: The exponent 'floatl' number. * \return A 'floatl' number representing 'x' raised to the power of 'y'. */ floatl floatl_pow(floatl x, floatl y) { return floatl_exp(floatl_mul(y, floatl_ln(x))); }