char float_tanh( floatnum x, int digits) { signed char sgn; if (!chckmathparam(x, digits)) return 0; sgn = float_getsign(x); float_abs(x); if (float_cmp(x, &c1Div2) >= 0) _tanhgt0_5(x, digits); else _tanhlt0_5(x, digits); float_setsign(x, sgn); return 1; }
char float_raise( floatnum power, cfloatnum base, cfloatnum exponent, int digits) { signed char sgn; if (float_isnan(exponent) || float_isnan(base)) return _seterror(power, NoOperand); if (digits <= 0 || digits > MATHPRECISION) return _seterror(power, InvalidPrecision); if (float_iszero(base)) { switch(float_getsign(exponent)) { case 0: return _seterror(power, OutOfDomain); case -1: return _seterror(power, ZeroDivide); } return _setzero(power); } sgn = float_getsign(base); if (sgn < 0) { if (!float_isinteger(exponent)) return _seterror(power, OutOfDomain); if ((float_getdigit(exponent, float_getexponent(exponent)) & 1) == 0) sgn = 1; } float_copy(power, base, digits+1); float_abs(power); if (!_raise(power, exponent, digits)) { float_seterror(Overflow); if (float_getexponent(base) * float_getsign(exponent) < 0) float_seterror(Underflow); return _setnan(power); } float_setsign(power, sgn); return 1; }
/* evaluates arctan x for all x. The result is in the range -pi/2 < result < pi/2 relative error for a 100 digit result is 9e-100 */ void _arctan( floatnum x, int digits) { signed char sgn; if (float_abscmp(x, &c1) > 0) { sgn = float_getsign(x); float_abs(x); float_reciprocal(x, digits); _arctanlt1(x, digits); float_sub(x, &cPiDiv2, x, digits+1); float_setsign(x, sgn); } else _arctanlt1(x, digits); }
void _logic2floatnum( floatnum f, t_longint* longint) { int idx; signed char sign; sign = _signextend(longint); if (sign < 0) _neg(longint); idx = MAXIDX; while (idx >= 0 && longint->value[idx] == 0) --idx; if (idx < 0) longint->length = 0; else longint->length = idx + 1; _longint2floatnum(f, longint); float_setsign(f, sign); }
/* evaluate sin x for |x| <= pi/4, using |sin x| = sqrt((1-cos x)*(2 + cos x-1)) relative error for 100 digit results is < 6e-100*/ void _sinltPiDiv4( floatnum x, int digits) { floatstruct tmp; signed char sgn; if (2*float_getexponent(x)+2 < -digits) /* for small x: sin x approx.== x */ return; float_create(&tmp); sgn = float_getsign(x); _cosminus1ltPiDiv4(x, digits); float_add(&tmp, x, &c2, digits+1); float_mul(x, x, &tmp, digits+1); float_abs(x); float_sqrt(x, digits); float_setsign(x, sgn); float_free(&tmp); }
/* evaluates sin x for |x| <= pi. */ void _sin( floatnum x, int digits) { signed char sgn; sgn = float_getsign(x); float_abs(x); if (float_cmp(x, &cPiDiv2) > 0) float_sub(x, &cPi, x, digits+1); if (float_cmp(x, &cPiDiv4) <= 0) _sinltPiDiv4(x, digits); else { float_sub(x, &cPiDiv2, x, digits+1); if (2*float_getexponent(x)+2 < - digits) float_setzero(x); else _cosminus1ltPiDiv4(x, digits); float_add(x, x, &c1, digits); } float_setsign(x, sgn); }
/* the Taylor series of arctan/arctanh x at x == 0. For small |x| < 0.01 this series converges very fast, yielding 4 or more digits of the result with every summand. The working precision is adjusted, so that the relative error for 100-digit arguments is around 5.0e-100. This means, the error is 1 in the 100-th place (or less) */ void arctanseries( floatnum x, int digits, char alternating) { int expx; int expsqrx; int pwrsz; int addsz; int i; floatstruct xsqr; floatstruct pwr; floatstruct smd; floatstruct sum; /* upper limit of log(x) and log(result) */ expx = float_getexponent(x)+1; /* the summands of the series from the second on are bounded by x^(2*i-1)/3. So the summation yields a result bounded by (x^3/(1-x*x))/3. For x < sqrt(1/3) approx.= 0.5, this is less than 0.5*x^3. We need to sum up only, if the first <digits> places of the result (roughly x) are touched. Ignoring the effect of a possile carry, this is only the case, if x*x >= 2*10^(-digits) > 10^(-digits) Example: for x = 9e-51, a 100-digits result covers the decimal places from 1e-51 to 1e-150. x^3/3 is roughly 3e-151, and so is the sum of the series. So we can ignore the sum, but we couldn't for x = 9e-50 */ if (float_iszero(x) || 2*expx < -digits) /* for very tiny arguments arctan/arctanh x is approx.== x */ return; float_create(&xsqr); float_create(&pwr); float_create(&smd); float_create(&sum); /* we adapt the working precision to the decreasing summands, saving time when multiplying. Unfortunately, there is no error bound given for the operations of bc_num. Tests show, that the last digit in an incomplete multiplication is usually not correct up to 5 ULP's. */ pwrsz = digits + 2*expx + 1; /* the precision of the addition must not decrease, of course */ addsz = pwrsz; i = 3; float_mul(&xsqr, x, x, pwrsz); float_setsign(&xsqr, alternating? -1 : 1); expsqrx = float_getexponent(&xsqr); float_copy(&pwr, x, pwrsz); float_setzero(&sum); for(; pwrsz > 0; ) { /* x^i */ float_mul(&pwr, &pwr, &xsqr, pwrsz+1); /* x^i/i */ float_divi(&smd, &pwr, i, pwrsz); /* The addition virtually does not introduce errors */ float_add(&sum, &sum, &smd, addsz); /* reduce the working precision according to the decreasing powers */ pwrsz = digits - expx + float_getexponent(&smd) + expsqrx + 3; i += 2; } /* add the first summand */ float_add(x, x, &sum, digits+1); float_free(&xsqr); float_free(&pwr); float_free(&smd); float_free(&sum); }