Skip to content

Commit

Permalink
adding native quad-double trigonometry function approximations
Browse files Browse the repository at this point in the history
  • Loading branch information
Ravenwater committed Aug 31, 2024
1 parent a29c087 commit 54c4d3d
Show file tree
Hide file tree
Showing 7 changed files with 1,178 additions and 29 deletions.
274 changes: 274 additions & 0 deletions include/universal/number/dd/math/cos_table.hpp

Large diffs are not rendered by default.

277 changes: 277 additions & 0 deletions include/universal/number/dd/math/sin_table.hpp

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions include/universal/number/qd/math/hyperbolic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace sw { namespace universal {

#if !QUADDOUBLE_NATIVE_HYPERBOLIC
// hyperbolic sine of an angle of x radians
inline qd sinh(qd x) {
return qd(std::sinh(double(x)));
Expand Down Expand Up @@ -38,4 +39,94 @@ namespace sw { namespace universal {
return qd(std::asinh(double(x)));
}

#else

inline qd sinh(const qd& a) {
if (a.iszero()) return 0.0;

if (abs(a) > 0.05) {
qd ea = exp(a);
return mul_pwr2(ea - reciprocal(ea), 0.5);
}

/* Since a is small, using the above formula gives
a lot of cancellation. So use Taylor series. */
qd s = a;
qd t = a;
qd r = sqr(t);
double m = 1.0;
double thresh = std::abs(double(a) * qd_eps);

do {
m += 2.0;
t *= r;
t /= (m - 1) * m;

s += t;
} while (abs(t) > thresh);

return s;
}

inline qd cosh(const qd& a) {
if (a.iszero()) return 1.0;

qd ea = exp(a);
return mul_pwr2(ea + reciprocal(ea), 0.5);
}

inline qd tanh(const qd& a) {
if (a.iszero()) return 0.0;

if (std::abs(double(a)) > 0.05) {
qd ea = exp(a);
qd inv_ea = reciprocal(ea);
return (ea - inv_ea) / (ea + inv_ea);
}
else {
qd s, c;
s = sinh(a);
c = sqrt(1.0 + sqr(s));
return s / c;
}
}

inline void sincosh(const qd& a, qd& s, qd& c) {
if (std::abs(double(a)) <= 0.05) {
s = sinh(a);
c = sqrt(1.0 + sqr(s));
}
else {
qd ea = exp(a);
qd inv_ea = reciprocal(ea);
s = mul_pwr2(ea - inv_ea, 0.5);
c = mul_pwr2(ea + inv_ea, 0.5);
}
}

inline qd log(const qd&);
inline qd asinh(const qd& a) {
return log(a + sqrt(sqr(a) + 1.0));
}

inline qd acosh(const qd& a) {
if (a < 1.0) {
std::cerr << "(acosh): Argument out of domain\n";
return qd(SpecificValue::snan);
}

return log(a + sqrt(sqr(a) - 1.0));
}

inline qd atanh(const qd& a) {
if (abs(a) >= 1.0) {
std::cerr << "(atanh): Argument out of domain\n";
return qd(SpecificValue::snan);
}

return mul_pwr2(log((1.0 + a) / (1.0 - a)), 0.5);
}

#endif

}} // namespace sw::universal
15 changes: 15 additions & 0 deletions include/universal/number/qd/math/minmax.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace sw { namespace universal {

#if !QUADDOUBLE_NATIVE_MINMAX

inline qd min(const qd& x, const qd& y) {
return qd(std::min(double(x), double(y)));
}
Expand All @@ -16,4 +18,17 @@ namespace sw { namespace universal {
return qd(std::max(double(x), double(y)));
}

#else
//////////////////////// logic functions /////////////////////////////////

inline qd max(const qd& a, const qd& b) { return (a > b) ? a : b; }

inline qd max(const qd& a, const qd& b, const qd& c) { return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); }

inline qd min(const qd& a, const qd& b) { return (a < b) ? a : b; }

inline qd min(const qd& a, const qd& b, const qd& c) { return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); }

#endif

}} // namespace sw::universal
69 changes: 45 additions & 24 deletions include/universal/number/qd/math/sqrt.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once
// sqrt.hpp: sqrt functions for quad-double (qd) floats
//
// algorithm courtesy of Scibuilders, Jack Poulson
//
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
// SPDX-License-Identifier: MIT
//
Expand All @@ -15,36 +17,49 @@ namespace sw { namespace universal {

#if QUADDOUBLE_NATIVE_SQRT

// Computes the square root of the quad-double number qd.
// NOTE: qd must be a non-negative number
inline qd sqrt(qd const &a) {
/* Strategy: Use Karp's trick: if x is an approximation
to sqrt(a), then
/// <summary>
/// sqrt returns the square root of its input, returns NaN if argument is negative
/// </summary>
/// <param name="a">input</param>
/// <returns>sqrt(a) or NaN</returns>
qd sqrt(const qd& a) {
/* Strategy:
sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx)
Perform the following Newton iteration:
The approximation is accurate to twice the accuracy of x.
Also, the multiplication (a*x) and [-]*x can be done with
only half the precision.
*/
x' = x + (1 - a * x^2) * x / 2;
if (a.iszero()) return qd{};
which converges to 1/sqrt(a), starting with the
double precision approximation to 1/sqrt(a).
Since Newton's iteration more or less doubles the
number of correct digits, we only need to perform it
twice.
*/

#if QUADDOUBLE_THROW_ARITHMETIC_EXCEPTION
if (a.isneg()) throw qd_negative_sqrt_arg();
#else
if (a.isneg()) std::cerr << "quad-double argument to sqrt is negative: " << a << std::endl;
if (a.isneg()) {
std::cerr << "quad-double argument to sqrt is negative\n";
return qd(SpecificValue::snan);
}
#endif

double x = 1.0 / std::sqrt(a[0]);
double ax = a[0] * x;
return aqd(ax, (a - sqr(qd(ax)))[0] * (x * 0.5));
}
qd r = (1.0 / std::sqrt(a[0]));
qd h = mul_pwr2(a, 0.5);

r += ((0.5 - h * sqr(r)) * r);
r += ((0.5 - h * sqr(r)) * r);
r += ((0.5 - h * sqr(r)) * r);

r *= a;
return r;
}

#else

// sqrt shim for quad-double
inline qd sqrt(qd const& a) {
inline qd sqrt(const qd& a) {
#if QUADDOUBLE_THROW_ARITHMETIC_EXCEPTION
if (a.isneg()) throw qd_negative_sqrt_arg();
#else // ! QUADDOUBLE_THROW_ARITHMETIC_EXCEPTION
Expand All @@ -57,15 +72,20 @@ namespace sw { namespace universal {
#endif // ! QUADDOUBLE_NATIVE_SQRT

// reciprocal sqrt
inline qd rsqrt(qd const& a) {
inline qd rsqrt(const qd& a) {
qd v = sqrt(a);
return reciprocal(v);
}


/* Computes the n-th root of the quad-double number a.
NOTE: n must be a positive integer.
NOTE: If n is even, then a must not be negative. */
/// <summary>
/// nroot returns the n-th root of its argument
/// n must be a positive integer.
/// If n is even, then argument _a_ must not be negative.
/// </summary>
/// <param name="a">input</param>
/// <param name="n">n-th root to get</param>
/// <returns>n-th root of (a) or NaN</returns>
inline qd nroot(const qd& a, int n) {
/* Strategy: Use Newton iteration for the function
Expand All @@ -76,7 +96,7 @@ namespace sw { namespace universal {
x' = x + x * (1 - a * x^n) / n
which converges quadratically. We can then find
a^{1/n} by taking the reciprocal.
a^{1/n} by taking the reciprocal.
*/

#if QUADDOUBLE_THROW_ARITHMETIC_EXCEPTION
Expand All @@ -86,11 +106,12 @@ namespace sw { namespace universal {

#else // ! QUADDOUBLE_THROW_ARITHMETIC_EXCEPTION
if (n <= 0) {
std::cerr << "quad-double nroot argument is negative: " << n << std::endl;
std::cerr << "quad-double nroot argument is negative\n";
return qd(SpecificValue::snan);
}

if (n % 2 == 0 && a.isneg()) {
std::cerr << "quad-double nroot argument is negative: " << n << std::endl;
std::cerr << "quad-double nroot argument is negative\n";
return qd(SpecificValue::snan);
}

Expand Down
Loading

0 comments on commit 54c4d3d

Please sign in to comment.