diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibM/math.cpp | 771 |
1 files changed, 154 insertions, 617 deletions
diff --git a/Userland/Libraries/LibM/math.cpp b/Userland/Libraries/LibM/math.cpp index adba07ede2..50d3ffbb67 100644 --- a/Userland/Libraries/LibM/math.cpp +++ b/Userland/Libraries/LibM/math.cpp @@ -6,6 +6,7 @@ */ #include <AK/ExtraMathConstants.h> +#include <AK/Math.h> #include <AK/Platform.h> #include <AK/StdLibExtras.h> #include <LibC/assert.h> @@ -345,124 +346,196 @@ long double nanl(const char* s) NOEXCEPT return __builtin_nanl(s); } -double trunc(double x) NOEXCEPT +#define MAKE_AK_BACKED1(name) \ + long double name##l(long double arg) NOEXCEPT \ + { \ + return AK::name<long double>(arg); \ + } \ + double name(double arg) NOEXCEPT \ + { \ + return AK::name<double>(arg); \ + } \ + float name##f(float arg) NOEXCEPT \ + { \ + return AK::name<float>(arg); \ + } +#define MAKE_AK_BACKED2(name) \ + long double name##l(long double arg1, long double arg2) NOEXCEPT \ + { \ + return AK::name<long double>(arg1, arg2); \ + } \ + double name(double arg1, double arg2) NOEXCEPT \ + { \ + return AK::name<double>(arg1, arg2); \ + } \ + float name##f(float arg1, float arg2) NOEXCEPT \ + { \ + return AK::name<float>(arg1, arg2); \ + } + +MAKE_AK_BACKED1(sin); +MAKE_AK_BACKED1(cos); +MAKE_AK_BACKED1(tan); +MAKE_AK_BACKED1(asin); +MAKE_AK_BACKED1(acos); +MAKE_AK_BACKED1(atan); +MAKE_AK_BACKED1(sinh); +MAKE_AK_BACKED1(cosh); +MAKE_AK_BACKED1(tanh); +MAKE_AK_BACKED1(asinh); +MAKE_AK_BACKED1(acosh); +MAKE_AK_BACKED1(atanh); +MAKE_AK_BACKED1(sqrt); +MAKE_AK_BACKED1(cbrt); +MAKE_AK_BACKED1(log); +MAKE_AK_BACKED1(log2); +MAKE_AK_BACKED1(log10); +MAKE_AK_BACKED1(exp); +MAKE_AK_BACKED1(exp2); +MAKE_AK_BACKED1(fabs); + +MAKE_AK_BACKED2(atan2); +MAKE_AK_BACKED2(hypot); +MAKE_AK_BACKED2(fmod); +MAKE_AK_BACKED2(pow); +MAKE_AK_BACKED2(remainder); + +long double truncl(long double x) NOEXCEPT { + if (fabsl(x) < LONG_LONG_MAX) { + // This is 1.6 times faster than the implemenation using the "internal_to_integer" + // helper (on x86_64) + // https://quick-bench.com/q/xBmxuY8am9qibSYVna90Y6PIvqA + u64 temp; + asm( + "fisttpq %[temp]\n" + "fildq %[temp]" + : "+t"(x) + : [temp] "m"(temp)); + return x; + } + return internal_to_integer(x, RoundingMode::ToZero); } -float truncf(float x) NOEXCEPT +double trunc(double x) NOEXCEPT { + if (fabs(x) < LONG_LONG_MAX) { + u64 temp; + asm( + "fisttpq %[temp]\n" + "fildq %[temp]" + : "+t"(x) + : [temp] "m"(temp)); + return x; + } + return internal_to_integer(x, RoundingMode::ToZero); } -long double truncl(long double x) NOEXCEPT +float truncf(float x) NOEXCEPT { + if (fabsf(x) < LONG_LONG_MAX) { + u64 temp; + asm( + "fisttpq %[temp]\n" + "fildq %[temp]" + : "+t"(x) + : [temp] "m"(temp)); + return x; + } + return internal_to_integer(x, RoundingMode::ToZero); } -long double cosl(long double angle) NOEXCEPT +long double rintl(long double value) { - long double ret = 0.0; + double res; asm( - "fcos" - : "=t"(ret) - : "0"(angle)); - return ret; + "frndint\n" + : "=t"(res) + : "0"(value)); + return res; } - -double cos(double angle) NOEXCEPT +double rint(double value) { - double ret = 0.0; + double res; asm( - "fcos" - : "=t"(ret) - : "0"(angle)); - - return ret; + "frndint\n" + : "=t"(res) + : "0"(value)); + return res; } - -float cosf(float angle) NOEXCEPT +float rintf(float value) { - float ret = 0.0; + double res; asm( - "fcos" - : "=t"(ret) - : "0"(angle)); - - return ret; + "frndint\n" + : "=t"(res) + : "0"(value)); + return res; } -long double sinl(long double angle) NOEXCEPT +long lrintl(long double value) { - long double ret = 0.0; + long res; asm( - "fsin" - : "=t"(ret) - : "0"(angle)); - - return ret; + "fistpl %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } - -// This can also be done with a taylor expansion, but for -// now this works pretty well (and doesn't mess anything up -// in quake in particular, which is very Floating-Point precision -// heavy) -double sin(double angle) NOEXCEPT +long lrint(double value) { - double ret = 0.0; + long res; asm( - "fsin" - : "=t"(ret) - : "0"(angle)); - - return ret; + "fistpl %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } - -float sinf(float angle) NOEXCEPT +long lrintf(float value) { - float ret = 0.0f; + long res; asm( - "fsin" - : "=t"(ret) - : "0"(angle)); - return ret; + "fistpl %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } -long double powl(long double x, long double y) NOEXCEPT +long long llrintl(long double value) { - // FIXME: Please fix me. I am naive. - if (isnan(y)) - return y; - if (y == 0) - return 1; - if (x == 0) - return 0; - if (y == 1) - return x; - int y_as_int = (int)y; - if (y == (long double)y_as_int) { - long double result = x; - for (int i = 0; i < fabsl(y) - 1; ++i) - result *= x; - if (y < 0) - result = 1.0l / result; - return result; - } - if (x < 0) { - return 1.l / exp2l(y * log2l(-x)); - } - - return exp2l(y * log2l(x)); + long long res; + asm( + "fistpq %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } - -double pow(double x, double y) NOEXCEPT +long long llrint(double value) { - return (double)powl(x, y); + long long res; + asm( + "fistpq %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } - -float powf(float x, float y) NOEXCEPT +long long llrintf(float value) { - return (float)powl(x, y); + long long res; + asm( + "fistpq %0\n" + : "+m"(res) + : "t"(value) + : "st"); + return res; } // On systems where FLT_RADIX == 2, ldexp is equivalent to scalbn @@ -481,27 +554,6 @@ float ldexpf(float x, int exp) NOEXCEPT return internal_scalbn(x, exp); } -long double tanhl(long double x) NOEXCEPT -{ - if (x > 0) { - long double exponentiated = expl(2 * x); - return (exponentiated - 1) / (exponentiated + 1); - } - long double plusX = expl(x); - long double minusX = 1 / plusX; - return (plusX - minusX) / (plusX + minusX); -} - -double tanh(double x) NOEXCEPT -{ - return (double)tanhl(x); -} - -float tanhf(float x) NOEXCEPT -{ - return (float)tanhl(x); -} - [[maybe_unused]] static long double ampsin(long double angle) NOEXCEPT { long double looped_angle = fmodl(M_PI + angle, M_TAU) - M_PI; @@ -519,345 +571,6 @@ float tanhf(float x) NOEXCEPT return quadratic_term + linear_term; } -long double tanl(long double angle) NOEXCEPT -{ - long double ret = 0.0, one; - __asm__( - "fptan" - : "=t"(one), "=u"(ret) - : "0"(angle)); - - return ret; -} - -double tan(double angle) NOEXCEPT -{ - return (double)tanl(angle); -} - -float tanf(float angle) NOEXCEPT -{ - return (float)tanl(angle); -} - -long double sqrtl(long double x) NOEXCEPT -{ - long double res; - asm("fsqrt" - : "=t"(res) - : "0"(x)); - return res; -} - -double sqrt(double x) NOEXCEPT -{ - double res; - __asm__("fsqrt" - : "=t"(res) - : "0"(x)); - return res; -} - -float sqrtf(float x) NOEXCEPT -{ - float res; - __asm__("fsqrt" - : "=t"(res) - : "0"(x)); - return res; -} - -long double sinhl(long double x) NOEXCEPT -{ - long double exponentiated = expl(x); - if (x > 0) - return (exponentiated * exponentiated - 1) / 2 / exponentiated; - return (exponentiated - 1 / exponentiated) / 2; -} - -double sinh(double x) NOEXCEPT -{ - return (double)sinhl(x); -} - -float sinhf(float x) NOEXCEPT -{ - return (float)sinhl(x); -} - -long double log10l(long double x) NOEXCEPT -{ - long double ret = 0.0l; - __asm__( - "fldlg2\n" - "fld %%st(1)\n" - "fyl2x\n" - "fstp %%st(1)" - : "=t"(ret) - : "0"(x)); - return ret; -} - -double log10(double x) NOEXCEPT -{ - return (double)log10l(x); -} - -float log10f(float x) NOEXCEPT -{ - return (float)log10l(x); -} - -long double logl(long double x) NOEXCEPT -{ - long double ret = 0.0l; - asm( - "fldln2\n" - "fld %%st(1)\n" - "fyl2x\n" - "fstp %%st(1)" - : "=t"(ret) - : "0"(x)); - return ret; -} - -double log(double x) NOEXCEPT -{ - return (double)logl(x); -} - -float logf(float x) NOEXCEPT -{ - return (float)logl(x); -} - -long double fmodl(long double index, long double period) NOEXCEPT -{ - return index - truncl(index / period) * period; -} - -double fmod(double index, double period) NOEXCEPT -{ - return index - trunc(index / period) * period; -} - -float fmodf(float index, float period) NOEXCEPT -{ - return index - truncf(index / period) * period; -} - -// FIXME: These aren't exactly like fmod, but these definitions are probably good enough for now -long double remainderl(long double x, long double y) NOEXCEPT -{ - return fmodl(x, y); -} - -double remainder(double x, double y) NOEXCEPT -{ - return fmod(x, y); -} - -float remainderf(float x, float y) NOEXCEPT -{ - return fmodf(x, y); -} - -long double expl(long double exponent) NOEXCEPT -{ - long double res = 0; - asm("fldl2e\n" - "fmulp\n" - "fld1\n" - "fld %%st(1)\n" - "fprem\n" - "f2xm1\n" - "faddp\n" - "fscale\n" - "fstp %%st(1)" - : "=t"(res) - : "0"(exponent)); - return res; -} - -double exp(double exponent) NOEXCEPT -{ - return (double)expl(exponent); -} - -float expf(float exponent) NOEXCEPT -{ - return (float)expl(exponent); -} - -long double exp2l(long double exponent) NOEXCEPT -{ - long double res = 0; - asm("fld1\n" - "fld %%st(1)\n" - "fprem\n" - "f2xm1\n" - "faddp\n" - "fscale\n" - "fstp %%st(1)" - : "=t"(res) - : "0"(exponent)); - return res; -} - -double exp2(double exponent) NOEXCEPT -{ - return (double)exp2l(exponent); -} - -float exp2f(float exponent) NOEXCEPT -{ - return (float)exp2l(exponent); -} - -long double coshl(long double x) NOEXCEPT -{ - long double exponentiated = expl(-x); - if (x < 0) - return (1 + exponentiated * exponentiated) / 2 / exponentiated; - return (1 / exponentiated + exponentiated) / 2; -} - -double cosh(double x) NOEXCEPT -{ - return (double)coshl(x); -} - -float coshf(float x) NOEXCEPT -{ - return (float)coshl(x); -} - -long double atan2l(long double y, long double x) NOEXCEPT -{ - long double result = 0; //atanl(y / x); - asm("fpatan" - : "=t"(result) - : "0"(x), "u"(y) - : "st(1)"); - return result; -} - -double atan2(double y, double x) NOEXCEPT -{ - double result = 0; //atanl(y / x); - asm("fpatan" - : "=t"(result) - : "0"(x), "u"(y) - : "st(1)"); - return result; -} -float atan2f(float y, float x) NOEXCEPT -{ - float result = 0; //atanl(y / x); - asm("fpatan" - : "=t"(result) - : "0"(x), "u"(y) - : "st(1)"); - return result; -} - -long double atanl(long double x) NOEXCEPT -{ - asm( - "fld1\n" - "fpatan\n" - : "=t"(x) - : "0"(x)); - return x; -} - -double atan(double x) NOEXCEPT -{ - asm( - "fld1\n" - "fpatan\n" - : "=t"(x) - : "0"(x)); - return x; -} - -float atanf(float x) NOEXCEPT -{ - asm( - "fld1\n" - "fpatan\n" - : "=t"(x) - : "0"(x)); - return x; -} - -long double asinl(long double x) NOEXCEPT -{ - if (x > 1 || x < -1) - return NAN; - if (x > 0.5 || x < -0.5) - return 2 * atanl(x / (1 + sqrtl(1 - x * x))); - long double squared = x * x; - long double value = x; - long double i = x * squared; - value += i * product_odd<1>() / product_even<2>() / 3; - i *= squared; - value += i * product_odd<3>() / product_even<4>() / 5; - i *= squared; - value += i * product_odd<5>() / product_even<6>() / 7; - i *= squared; - value += i * product_odd<7>() / product_even<8>() / 9; - i *= squared; - value += i * product_odd<9>() / product_even<10>() / 11; - i *= squared; - value += i * product_odd<11>() / product_even<12>() / 13; - i *= squared; - value += i * product_odd<13>() / product_even<14>() / 15; - i *= squared; - value += i * product_odd<15>() / product_even<16>() / 17; - return value; -} - -double asin(double x) NOEXCEPT -{ - return (double)asinl(x); -} - -float asinf(float x) NOEXCEPT -{ - return (float)asinl(x); -} - -long double acosl(long double x) NOEXCEPT -{ - return M_PI_2 - asinl(x); -} - -double acos(double x) NOEXCEPT -{ - return M_PI_2 - asin(x); -} - -float acosf(float x) NOEXCEPT -{ - return static_cast<float>(M_PI_2) - asinf(x); -} - -long double fabsl(long double value) NOEXCEPT -{ - return value < 0 ? -value : value; -} - -double fabs(double value) NOEXCEPT -{ - return value < 0 ? -value : value; -} - -float fabsf(float value) NOEXCEPT -{ - return value < 0 ? -value : value; -} - int ilogbl(long double x) NOEXCEPT { return internal_ilogb(x); @@ -888,29 +601,6 @@ float logbf(float x) NOEXCEPT return ilogbf(x); } -long double log2l(long double x) NOEXCEPT -{ - long double ret = 0.0; - asm( - "fld1\n" - "fld %%st(1)\n" - "fyl2x\n" - "fstp %%st(1)" - : "=t"(ret) - : "0"(x)); - return ret; -} - -double log2(double x) NOEXCEPT -{ - return (double)log2l(x); -} - -float log2f(float x) NOEXCEPT -{ - return (float)log2l(x); -} - double frexp(double x, int* exp) NOEXCEPT { *exp = (x == 0) ? 0 : (1 + ilogb(x)); @@ -989,51 +679,6 @@ long double floorl(long double value) NOEXCEPT return internal_to_integer(value, RoundingMode::Down); } -long double rintl(long double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} - -double rint(double value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} - -float rintf(float value) NOEXCEPT -{ - return internal_to_integer(value, RoundingMode { fegetround() }); -} - -long lrintl(long double value) NOEXCEPT -{ - return (long)internal_to_integer(value, RoundingMode { fegetround() }); -} - -long lrint(double value) NOEXCEPT -{ - return (long)internal_to_integer(value, RoundingMode { fegetround() }); -} - -long lrintf(float value) NOEXCEPT -{ - return (long)internal_to_integer(value, RoundingMode { fegetround() }); -} - -long long llrintl(long double value) NOEXCEPT -{ - return (long long)internal_to_integer(value, RoundingMode { fegetround() }); -} - -long long llrint(double value) NOEXCEPT -{ - return (long long)internal_to_integer(value, RoundingMode { fegetround() }); -} - -long long llrintf(float value) NOEXCEPT -{ - return (long long)internal_to_integer(value, RoundingMode { fegetround() }); -} - float ceilf(float value) NOEXCEPT { return internal_to_integer(value, RoundingMode::Up); @@ -1150,54 +795,6 @@ float expm1f(float x) NOEXCEPT return expf(x) - 1; } -long double cbrtl(long double x) NOEXCEPT -{ - if (isinf(x) || x == 0) - return x; - if (x < 0) - return -cbrtl(-x); - - long double r = x; - long double ex = 0; - - while (r < 0.125l) { - r *= 8; - ex--; - } - while (r > 1.0l) { - r *= 0.125l; - ex++; - } - - r = (-0.46946116l * r + 1.072302l) * r + 0.3812513l; - - while (ex < 0) { - r *= 0.5l; - ex++; - } - while (ex > 0) { - r *= 2.0l; - ex--; - } - - r = (2.0l / 3.0l) * r + (1.0l / 3.0l) * x / (r * r); - r = (2.0l / 3.0l) * r + (1.0l / 3.0l) * x / (r * r); - r = (2.0l / 3.0l) * r + (1.0l / 3.0l) * x / (r * r); - r = (2.0l / 3.0l) * r + (1.0l / 3.0l) * x / (r * r); - - return r; -} - -double cbrt(double x) NOEXCEPT -{ - return (double)cbrtl(x); -} - -float cbrtf(float x) NOEXCEPT -{ - return (float)cbrtl(x); -} - long double log1pl(long double x) NOEXCEPT { return logl(1 + x); @@ -1213,66 +810,6 @@ float log1pf(float x) NOEXCEPT return logf(1 + x); } -long double acoshl(long double x) NOEXCEPT -{ - return logl(x + sqrtl(x * x - 1)); -} - -double acosh(double x) NOEXCEPT -{ - return log(x + sqrt(x * x - 1)); -} - -float acoshf(float x) NOEXCEPT -{ - return logf(x + sqrtf(x * x - 1)); -} - -long double asinhl(long double x) NOEXCEPT -{ - return logl(x + sqrtl(x * x + 1)); -} - -double asinh(double x) NOEXCEPT -{ - return log(x + sqrt(x * x + 1)); -} - -float asinhf(float x) NOEXCEPT -{ - return logf(x + sqrtf(x * x + 1)); -} - -long double atanhl(long double x) NOEXCEPT -{ - return logl((1 + x) / (1 - x)) / 2.0l; -} - -double atanh(double x) NOEXCEPT -{ - return log((1 + x) / (1 - x)) / 2.0; -} - -float atanhf(float x) NOEXCEPT -{ - return logf((1 + x) / (1 - x)) / 2.0f; -} - -long double hypotl(long double x, long double y) NOEXCEPT -{ - return sqrtl(x * x + y * y); -} - -double hypot(double x, double y) NOEXCEPT -{ - return sqrt(x * x + y * y); -} - -float hypotf(float x, float y) NOEXCEPT -{ - return sqrtf(x * x + y * y); -} - long double erfl(long double x) NOEXCEPT { // algorithm taken from Abramowitz and Stegun (no. 26.2.17) |