diff --git a/calcerr.tbl b/calcerr.tbl index eb1db3a..618d329 100644 --- a/calcerr.tbl +++ b/calcerr.tbl @@ -496,3 +496,7 @@ E_ATANH3 Invalid value for calculating atan E_ACOTH3 Invalid value for calculating acot E_ASECH3 Invalid value for calculating asec E_ACSCH3 Invalid value for calculating acsc +E_D2R1 Bad epsilon for converting degrees to radians +E_D2R2 Bad first argument converting degrees to radians +E_R2D1 Bad epsilon for converting radians to degrees +E_R2D2 Bad first argument converting radians to degrees diff --git a/func.c b/func.c index 65f3bb2..4cf2901 100644 --- a/func.c +++ b/func.c @@ -2181,6 +2181,90 @@ f_cos(int count, VALUE **vals) } +/* + * f_d2r - convert degrees to radians + */ +S_FUNC VALUE +f_d2r(int count, VALUE **vals) +{ + VALUE result; + NUMBER *eps; + NUMBER *pidiv180; + + /* initialize VALUE */ + result.v_subtype = V_NOSUBTYPE; + + /* firewall */ + eps = conf->epsilon; + if (count == 2) { + if (vals[1]->v_type != V_NUM || qiszero(vals[1]->v_num)) + return error_value(E_D2R1); + eps = vals[1]->v_num; + } + + /* calculate argument * (pi/180) */ + switch (vals[0]->v_type) { + case V_NUM: + pidiv180 = qpidiv180(eps); + result.v_num = qmul(vals[0]->v_num, pidiv180); + result.v_type = V_NUM; + qfree(pidiv180); + break; + case V_COM: + pidiv180 = qpidiv180(eps); + result.v_com = c_mulq(vals[0]->v_com, pidiv180); + result.v_type = V_COM; + qfree(pidiv180); + break; + default: + return error_value(E_D2R2); + } + return result; +} + + +/* + * f_r2d - convert radians to degrees + */ +S_FUNC VALUE +f_r2d(int count, VALUE **vals) +{ + VALUE result; + NUMBER *eps; + NUMBER *pidiv180; + + /* initialize VALUE */ + result.v_subtype = V_NOSUBTYPE; + + /* firewall */ + eps = conf->epsilon; + if (count == 2) { + if (vals[1]->v_type != V_NUM || qiszero(vals[1]->v_num)) + return error_value(E_R2D1); + eps = vals[1]->v_num; + } + + /* calculate argument / (pi/180) */ + switch (vals[0]->v_type) { + case V_NUM: + pidiv180 = qpidiv180(eps); + result.v_num = qqdiv(vals[0]->v_num, pidiv180); + result.v_type = V_NUM; + qfree(pidiv180); + break; + case V_COM: + pidiv180 = qpidiv180(eps); + result.v_com = c_divq(vals[0]->v_com, pidiv180); + result.v_type = V_COM; + qfree(pidiv180); + break; + default: + return error_value(E_R2D2); + } + return result; +} + + S_FUNC VALUE f_sin(int count, VALUE **vals) { @@ -8670,6 +8754,8 @@ STATIC CONST struct builtin builtins[] = { "date and time as string"}, {"custom", 0, IN, 0, OP_NOP, 0, f_custom, "custom builtin function interface"}, + {"d2r", 1, 2, 0, OP_NOP, 0, f_d2r, + "convert degrees to radians"}, {"delete", 2, 2, FA, OP_NOP, 0, f_listdelete, "delete element from list a at position b"}, {"den", 1, 1, 0, OP_DENOMINATOR, qden, 0, @@ -9027,6 +9113,8 @@ STATIC CONST struct builtin builtins[] = { {"quomod", 4, 5, FA, OP_NOP, 0, f_quomod, "set c and d to quotient and remainder of a\n" "\t\t\tdivided by b"}, + {"r2d", 1, 2, 0, OP_NOP, 0, f_r2d, + "convert radians to degrees"}, {"rand", 0, 2, 0, OP_NOP, f_rand, 0, "additive 55 random number [0,2^64), [0,a), or [a,b)"}, {"randbit", 0, 1, 0, OP_NOP, f_randbit, 0, diff --git a/qmath.h b/qmath.h index b7782aa..b36a37c 100644 --- a/qmath.h +++ b/qmath.h @@ -213,6 +213,7 @@ E_FUNC NUMBER *qacsch(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qacoth(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qlegtoleg(NUMBER *q, NUMBER *epsilon, BOOL wantneg); E_FUNC NUMBER *qpi(NUMBER *epsilon); +E_FUNC NUMBER *qpidiv180(NUMBER *epsilon); E_FUNC NUMBER *qcatalan(NUMBER *); E_FUNC NUMBER *qbern(ZVALUE z); E_FUNC void qfreebern(void); diff --git a/qtrans.c b/qtrans.c index b15811c..07d163b 100644 --- a/qtrans.c +++ b/qtrans.c @@ -47,7 +47,26 @@ NUMBER _qlge_ = { { _qlgenum_, 1, 0 }, { _qlgeden_, 1, 0 }, 1, NULL }; STATIC NUMBER *ln_10 = NULL; STATIC NUMBER *ln_10_epsilon = NULL; -STATIC NUMBER *pivalue[2]; +/* + * cache pi + * + * pivalue[LAST_PI_EPSILON] - last epsilon used to calculate pi + * pivalue[LAST_PI_VALUE] - last calculated pi + * given pivalue[LAST_PI_EPSILON] epsilon + * pivalue[LAST_PI_DIV_180_EPSILON] last epsilon used to calculate pi/180 + * pivalue[LAST_PI_DIV_180] - last calculated pi/180 given + * pivalue[LAST_PI_DIV_180_EPSILON] epsilon + */ +#define PI_CACHE_LEN (4) +#define LAST_PI_EPSILON (0) +#define LAST_PI_VALUE (1) +#define LAST_PI_DIV_180_EPSILON (2) +#define LAST_PI_DIV_180 (3) +STATIC NUMBER *pivalue[PI_CACHE_LEN]; + +/* + * other static function decls + */ STATIC NUMBER *qexprel(NUMBER *q, long bitnum); /* @@ -725,16 +744,23 @@ qpi(NUMBER *epsilon) long bits; /* needed number of bits of precision */ long t; + /* firewall */ if (qiszero(epsilon)) { math_error("zero epsilon value for pi"); /*NOTREACHED*/ } - if (epsilon == pivalue[0]) - return qlink(pivalue[1]); - if (pivalue[0]) { - qfree(pivalue[0]); - qfree(pivalue[1]); + + /* use pi cache if epsilon marches, else flush if needed */ + if (epsilon == pivalue[LAST_PI_EPSILON]) { + return qlink(pivalue[LAST_PI_VALUE]); } + if (pivalue[LAST_PI_EPSILON]) { + qfree(pivalue[LAST_PI_EPSILON]); + } + if (pivalue[LAST_PI_VALUE]) { + qfree(pivalue[LAST_PI_VALUE]); + } + bits = -qilog2(epsilon) + 4; if (bits < 4) bits = 4; @@ -768,11 +794,54 @@ qpi(NUMBER *epsilon) zfree(sum); r = qmappr(t1, epsilon, 24L); qfree(t1); - pivalue[0] = qlink(epsilon); - pivalue[1] = qlink(r); + pivalue[LAST_PI_EPSILON] = qlink(epsilon); + pivalue[LAST_PI_VALUE] = qlink(r); return r; } + +/* + * qpidiv180 - calcucalte pi / 180 + * + * This function returns pi/180 as used to covert between degrees and radians. + */ +NUMBER * +qpidiv180(NUMBER *epsilon) +{ + NUMBER *pi, *pidiv180; + + /* firewall */ + if (qiszero(epsilon)) { + math_error("zero epsilon value for qpidiv180"); + /*NOTREACHED*/ + } + + /* use pi/180 cache if epsilon marches, else flush if needed */ + if (epsilon == pivalue[LAST_PI_DIV_180_EPSILON]) { + return qlink(pivalue[LAST_PI_DIV_180]); + } + if (pivalue[LAST_PI_DIV_180_EPSILON]) { + qfree(pivalue[LAST_PI_DIV_180_EPSILON]); + } + if (pivalue[LAST_PI_DIV_180]) { + qfree(pivalue[LAST_PI_DIV_180]); + } + + /* let qpi() returned cached pi or calculate new as needed */ + pi = qpi(epsilon); + + /* now calculate pi/180 */ + pidiv180 = qdivi(pi, 180); + + /* cache epsilon and pi/180 */ + pivalue[LAST_PI_DIV_180_EPSILON] = qlink(epsilon); + pivalue[LAST_PI_DIV_180] = qlink(pidiv180); + + /* return pi/180 */ + return pidiv180; +} + + /* * Calculate the exponential function to the nearest or next to nearest * multiple of the positive number epsilon.