diff --git a/calcerr.tbl b/calcerr.tbl index 618d329..d22a2f2 100644 --- a/calcerr.tbl +++ b/calcerr.tbl @@ -1,7 +1,7 @@ # # calcerr - error codes and messages # -# Copyright (C) 1999-2006 Ernest Bowen +# Copyright (C) 1999-2006,2021 Ernest Bowen # # Calc is open software; you can redistribute it and/or modify it under # the terms of the version 2.1 of the GNU Lesser General Public License @@ -500,3 +500,7 @@ 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 +E_G2R1 Bad epsilon for converting gradians to radians +E_G2R2 Bad first argument converting gradians to radians +E_R2G1 Bad epsilon for converting radians to gradians +E_R2G2 Bad first argument converting radians to gradians diff --git a/func.c b/func.c index 4cf2901..93dca01 100644 --- a/func.c +++ b/func.c @@ -2265,6 +2265,90 @@ f_r2d(int count, VALUE **vals) } +/* + * f_d2r - convert gradians to radians + */ +S_FUNC VALUE +f_g2r(int count, VALUE **vals) +{ + VALUE result; + NUMBER *eps; + NUMBER *pidiv200; + + /* 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_G2R1); + eps = vals[1]->v_num; + } + + /* calculate argument * (pi/200) */ + switch (vals[0]->v_type) { + case V_NUM: + pidiv200 = qpidiv200(eps); + result.v_num = qmul(vals[0]->v_num, pidiv200); + result.v_type = V_NUM; + qfree(pidiv200); + break; + case V_COM: + pidiv200 = qpidiv200(eps); + result.v_com = c_mulq(vals[0]->v_com, pidiv200); + result.v_type = V_COM; + qfree(pidiv200); + break; + default: + return error_value(E_G2R2); + } + return result; +} + + +/* + * f_r2g - convert radians to gradians + */ +S_FUNC VALUE +f_r2g(int count, VALUE **vals) +{ + VALUE result; + NUMBER *eps; + NUMBER *pidiv200; + + /* 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_R2G1); + eps = vals[1]->v_num; + } + + /* calculate argument / (pi/200) */ + switch (vals[0]->v_type) { + case V_NUM: + pidiv200 = qpidiv200(eps); + result.v_num = qqdiv(vals[0]->v_num, pidiv200); + result.v_type = V_NUM; + qfree(pidiv200); + break; + case V_COM: + pidiv200 = qpidiv200(eps); + result.v_com = c_divq(vals[0]->v_com, pidiv200); + result.v_type = V_COM; + qfree(pidiv200); + break; + default: + return error_value(E_R2G2); + } + return result; +} + + S_FUNC VALUE f_sin(int count, VALUE **vals) { @@ -8866,6 +8950,8 @@ STATIC CONST struct builtin builtins[] = { "return the file position"}, {"frac", 1, 1, 0, OP_FRAC, qfrac, 0, "fractional part of value"}, + {"g2r", 1, 2, 0, OP_NOP, 0, f_g2r, + "convert gradians to radians"}, {"gcd", 1, IN, 0, OP_NOP, f_gcd, 0, "greatest common divisor"}, {"gcdrem", 2, 2, 0, OP_NOP, qgcdrem, 0, @@ -9115,6 +9201,8 @@ STATIC CONST struct builtin builtins[] = { "\t\t\tdivided by b"}, {"r2d", 1, 2, 0, OP_NOP, 0, f_r2d, "convert radians to degrees"}, + {"r2g", 1, 2, 0, OP_NOP, 0, f_r2g, + "convert radians to gradians"}, {"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 b36a37c..f19f4d6 100644 --- a/qmath.h +++ b/qmath.h @@ -1,7 +1,7 @@ /* * qmath - declarations for extended precision rational arithmetic * - * Copyright (C) 1999-2007,2014 David I. Bell + * Copyright (C) 1999-2007,2014,2021 David I. Bell * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License @@ -214,6 +214,7 @@ 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 *qpidiv200(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 926facc..2da35ac 100644 --- a/qtrans.c +++ b/qtrans.c @@ -56,12 +56,17 @@ STATIC NUMBER *ln_10_epsilon = NULL; * pivalue[LAST_PI_DIV_180_EPSILON] - last epsilon used to calculate pi/180 * pivalue[LAST_PI_DIV_180_VALUE] - last calculated pi/180 given * pivalue[LAST_PI_DIV_180_EPSILON] epsilon + * pivalue[LAST_PI_DIV_200_EPSILON] - last epsilon used to calculate pi/200 + * pivalue[LAST_PI_DIV_200_VALUE] - last calculated pi/200 given + * pivalue[LAST_PI_DIV_200_EPSILON] epsilon */ enum pi_cache { LAST_PI_EPSILON = 0, LAST_PI_VALUE, LAST_PI_DIV_180_EPSILON, LAST_PI_DIV_180_VALUE, + LAST_PI_DIV_200_EPSILON, + LAST_PI_DIV_200_VALUE, PI_CACHE_LEN /* must be last */ }; STATIC NUMBER *pivalue[PI_CACHE_LEN] = { @@ -69,6 +74,8 @@ STATIC NUMBER *pivalue[PI_CACHE_LEN] = { NULL, /* LAST_PI_VALUE */ NULL, /* LAST_PI_DIV_180_EPSILON */ NULL, /* LAST_PI_DIV_180_VALUE */ + NULL, /* LAST_PI_DIV_200_EPSILON */ + NULL, /* LAST_PI_DIV_200_VALUE */ }; /* @@ -812,7 +819,7 @@ qpi(NUMBER *epsilon) /* * qpidiv180 - calcucalte pi / 180 * - * This function returns pi/180 as used to covert between degrees and radians. + * This function returns pi/180 as used to covert between radians and degrees. */ NUMBER * qpidiv180(NUMBER *epsilon) @@ -853,6 +860,50 @@ qpidiv180(NUMBER *epsilon) } +/* + * qpidiv200 - calcucalte pi / 200 + * + * This function returns pi/200 as used to covert between radians and gradians. + */ +NUMBER * +qpidiv200(NUMBER *epsilon) +{ + NUMBER *pi, *pidiv200; + + /* firewall */ + if (qiszero(epsilon)) { + math_error("zero epsilon value for qpidiv200"); + /*NOTREACHED*/ + } + + /* use pi/200 cache if epsilon marches, else flush if needed */ + if (pivalue[LAST_PI_DIV_200_EPSILON] != NULL && + pivalue[LAST_PI_DIV_200_VALUE] != NULL && + epsilon == pivalue[LAST_PI_DIV_200_EPSILON]) { + return qlink(pivalue[LAST_PI_DIV_200_VALUE]); + } + if (pivalue[LAST_PI_DIV_200_EPSILON] != NULL) { + qfree(pivalue[LAST_PI_DIV_200_EPSILON]); + } + if (pivalue[LAST_PI_DIV_200_VALUE] != NULL) { + qfree(pivalue[LAST_PI_DIV_200_VALUE]); + } + + /* let qpi() returned cached pi or calculate new as needed */ + pi = qpi(epsilon); + + /* calculate pi/200 */ + pidiv200 = qdivi(pi, 200); + + /* cache epsilon and pi/200 */ + pivalue[LAST_PI_DIV_200_EPSILON] = qlink(epsilon); + pivalue[LAST_PI_DIV_200_VALUE] = qlink(pidiv200); + + /* return pi/200 */ + return pidiv200; +} + + /* * Calculate the exponential function to the nearest or next to nearest * multiple of the positive number epsilon.