add log2(x [,eps]) builtin function

Added log2(x [,eps]) builtin function.  When x is an integer
power of 2, log2(x) will return an integer, otherwise it will
return the equivalent of ln(x)/ln(2).
This commit is contained in:
Landon Curt Noll
2023-08-27 19:02:37 -07:00
parent 56c568060a
commit 4e5fcc8812
15 changed files with 429 additions and 71 deletions

View File

@@ -80,6 +80,10 @@ The following are the changes from calc version 2.14.3.5 to date:
comments in "README.RELEASE", which serves as the contents comments in "README.RELEASE", which serves as the contents
of the calc command "help release". of the calc command "help release".
Added log2(x [,eps]) builtin function. When x is an integer
power of 2, log2(x) will return an integer, otherwise it will
return the equivalent of ln(x)/ln(2).
The following are the changes from calc version 2.14.3.4 to 2.14.3.5: The following are the changes from calc version 2.14.3.4 to 2.14.3.5:

View File

@@ -393,6 +393,12 @@ ZVALUE yourself. Examples of such checks are:
zge32b(z) (number is >= 2^32) zge32b(z) (number is >= 2^32)
zge64b(z) (number is >= 2^64) zge64b(z) (number is >= 2^64)
Return true if z an integer power of 2: set zlog2 to log base 2 of z.
Return false if a is NOT integer power of 2: set zlog2 to 0.
The zlog2 arg must be a non-NULL pointer to a ZVALUE.
zispowerof2(z, &zlog2)
Typically the largest unsigned long is typedefed to FULL. The following Typically the largest unsigned long is typedefed to FULL. The following
macros are useful in dealing with this data type: macros are useful in dealing with this data type:
@@ -524,7 +530,7 @@ macros are:
qistwo(q) (number is 2) qistwo(q) (number is 2)
qiseven(q) (number is an even integer) qiseven(q) (number is an even integer)
qisodd(q) (number is an odd integer) qisodd(q) (number is an odd integer)
qistwopower(q) (number is a power of 2 >= 1) qisreciprocal(q) (number is 1 / an integer and q != 0)
The comparisons for NUMBERs are similar to the ones for ZVALUEs. You use the The comparisons for NUMBERs are similar to the ones for ZVALUEs. You use the
qcmp and qrel functions. qcmp and qrel functions.

View File

@@ -1,7 +1,7 @@
/* /*
* regress - calc regression and correctness test suite * regress - calc regression and correctness test suite
* *
* Copyright (C) 1999-2017,2021 David I. Bell and Landon Curt Noll * Copyright (C) 1999-2017,2021,2023 David I. Bell and Landon Curt Noll
* *
* Calc is open software; you can redistribute it and/or modify it under * 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 * the terms of the version 2.1 of the GNU Lesser General Public License
@@ -2997,6 +2997,45 @@ define test_2600()
strcat(str(tnum++), strcat(str(tnum++),
': round(log(1.2+1.2i,1e-10),10) == ', ': round(log(1.2+1.2i,1e-10),10) == ',
'0.2296962439+0.3410940885i')); '0.2296962439+0.3410940885i'));
vrfy(log2(2) == 1,
strcat(str(tnum++), ': log2(2) == 1'));
vrfy(log2(4) == 2,
strcat(str(tnum++), ': log2(4) == 2'));
vrfy(log2(1024) == 10,
strcat(str(tnum++), ': log2(1024) == 10'));
vrfy(log2(2^500) == 500,
strcat(str(tnum++), ': log2(2^500) == 500'));
vrfy(log2(1/2^23209) == -23209,
strcat(str(tnum++), ': log2(1/2^23209) == -23209'));
vrfy(isint(log2(1/2^23209)),
strcat(str(tnum++), ': isint(log2(1/2^23209))'));
vrfy(round(log2(127),10) == 6.9886846868,
strcat(str(tnum++),
': round(log2(127),10) == 6.9886846868'));
vrfy(round(log2(23209),10) == 14.5023967426,
strcat(str(tnum++),
': round(log2(23209),10) == 14.5023967426'));
vrfy(round(log2(2^17-19),10) == 16.9997908539,
strcat(str(tnum++),
': round(log2(2^17-19),10) == 16.9997908539'));
vrfy(round(log2(-127),10) == 6.9886846868+4.5323601418i,
strcat(str(tnum++),
': round(log2(-127),10) == 6.9886846868+4.5323601418i'));
vrfy(round(log2(2+3i),10) == 1.8502198591+1.4178716307i,
strcat(str(tnum++),
': round(log2(2+3i),10) == 1.8502198591+1.4178716307i'));
vrfy(round(log2(-2+3i),10) == 1.8502198591+3.1144885111i,
strcat(str(tnum++),
': round(log2(-2+3i),10) == 1.8502198591+3.1144885111i'));
vrfy(round(log2(2-3i),10) == 1.8502198591-1.4178716307i,
strcat(str(tnum++),
': round(log2(2-3i),10) == 1.8502198591-1.4178716307i'));
vrfy(round(log2(-2-3i),10) == 1.8502198591-3.1144885111i,
strcat(str(tnum++),
': round(log2(-2-3i),10) == 1.8502198591-3.1144885111i'));
vrfy(round(log2(17+0.3i),10) == 4.0876874474+0.0254566819i,
strcat(str(tnum++),
': round(log2(17+0.3i),10) == 4.0876874474+0.0254566819i'));
epsilon(i),; epsilon(i),;
print tnum++: ': epsilon(i),;'; print tnum++: ': epsilon(i),;';

View File

@@ -1,7 +1,7 @@
# #
# calcerr - error codes and messages # calcerr - error codes and messages
# #
# Copyright (C) 1999-2006,2021 Ernest Bowen # Copyright (C) 1999-2006,2021,2023 Ernest Bowen and Landon Curt Noll
# #
# Calc is open software; you can redistribute it and/or modify it under # 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 # the terms of the version 2.1 of the GNU Lesser General Public License
@@ -542,3 +542,6 @@ E_HMS2H1 Non-real-number arguments 1, 2 or 3 for hms2h
E_HMS2H2 Invalid rounding arg 4 for hms2h E_HMS2H2 Invalid rounding arg 4 for hms2h
E_HM2H1 Non-real-number arguments 1 or 2 for hm2h E_HM2H1 Non-real-number arguments 1 or 2 for hm2h
E_HM2H2 Invalid rounding arg 4 for hm2h E_HM2H2 Invalid rounding arg 4 for hm2h
E_LOG2_1 Bad epsilon argument for log2
E_LOG2_2 Non-numeric first argument for log2
E_LOG2_3 Cannot calculate log2 for this value

View File

@@ -1,7 +1,7 @@
/* /*
* cmath - data structures for extended precision complex arithmetic * cmath - data structures for extended precision complex arithmetic
* *
* Copyright (C) 1999-2007,2014 David I. Bell * Copyright (C) 1999-2007,2014,2023 David I. Bell and Landon Curt Noll
* *
* Calc is open software; you can redistribute it and/or modify it under * 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 * the terms of the version 2.1 of the GNU Lesser General Public License
@@ -97,6 +97,7 @@ E_FUNC COMPLEX *c_root(COMPLEX *c, NUMBER *q, NUMBER *epsilon);
E_FUNC COMPLEX *c_exp(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_exp(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_ln(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_ln(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_log(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_log(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_log2(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_cos(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_cos(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_sin(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_sin(COMPLEX *c, NUMBER *epsilon);
E_FUNC COMPLEX *c_cosh(COMPLEX *c, NUMBER *epsilon); E_FUNC COMPLEX *c_cosh(COMPLEX *c, NUMBER *epsilon);

View File

@@ -1,7 +1,7 @@
/* /*
* comfunc - extended precision complex arithmetic non-primitive routines * comfunc - extended precision complex arithmetic non-primitive routines
* *
* Copyright (C) 1999-2007,2021-2023 David I. Bell and Ernest Bowen * Copyright (C) 1999-2007,2021-2023 David I. Bell, Landon Curt Noll and Ernest Bowen
* *
* Primary author: David I. Bell * Primary author: David I. Bell
* *
@@ -39,9 +39,13 @@
*/ */
STATIC COMPLEX *cln_10 = NULL; STATIC COMPLEX *cln_10 = NULL;
STATIC NUMBER *cln_10_epsilon = NULL; STATIC NUMBER *cln_10_epsilon = NULL;
STATIC COMPLEX *cln_2 = NULL;
STATIC NUMBER *cln_2_epsilon = NULL;
STATIC NUMBER _q10_ = { { _tenval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL }; STATIC NUMBER _q10_ = { { _tenval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
STATIC NUMBER _q2_ = { { _twoval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
STATIC NUMBER _q0_ = { { _zeroval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL }; STATIC NUMBER _q0_ = { { _zeroval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };
COMPLEX _cten_ = { &_q10_, &_q0_, 1 }; COMPLEX _cten_ = { &_q10_, &_q0_, 1 };
COMPLEX _ctwo_ = { &_q2_, &_q0_, 1 };
/* /*
@@ -530,6 +534,7 @@ c_ln(COMPLEX *c, NUMBER *epsilon)
return r; return r;
} }
/* /*
* Calculate base 10 logarithm by: * Calculate base 10 logarithm by:
* *
@@ -546,7 +551,7 @@ c_log(COMPLEX *c, NUMBER *epsilon)
* compute ln(c) first * compute ln(c) first
*/ */
ln_c = c_ln(c, epsilon); ln_c = c_ln(c, epsilon);
/* log(1) == 0 */ /* quick return for log(1) == 0 */
if (ciszero(ln_c)) { if (ciszero(ln_c)) {
return ln_c; return ln_c;
} }
@@ -585,6 +590,61 @@ c_log(COMPLEX *c, NUMBER *epsilon)
} }
/*
* Calculate base 2 logarithm by:
*
* log(c) = ln(c) / ln(2)
*/
COMPLEX *
c_log2(COMPLEX *c, NUMBER *epsilon)
{
int need_new_cln_2 = true; /* false => use cached cln_2 value */
COMPLEX *ln_c; /* ln(x) */
COMPLEX *ret; /* base 2 of x */
/*
* compute ln(c) first
*/
ln_c = c_ln(c, epsilon);
/* quick return for log(1) == 0 */
if (ciszero(ln_c)) {
return ln_c;
}
/*
* save epsilon for ln(2) if needed
*/
if (cln_2_epsilon == NULL) {
/* first time call */
cln_2_epsilon = qcopy(epsilon);
} else if (qcmp(cln_2_epsilon, epsilon) == true) {
/* replaced cached value with epsilon arg */
qfree(cln_2_epsilon);
cln_2_epsilon = qcopy(epsilon);
} else if (cln_2 != NULL) {
/* the previously computed ln(2) is OK to use */
need_new_cln_2 = false;
}
/*
* compute ln(2) if needed
*/
if (need_new_cln_2 == true) {
if (cln_2 != NULL) {
comfree(cln_2);
}
cln_2 = c_ln(&_ctwo_, cln_2_epsilon);
}
/*
* return ln(c) / ln(2)
*/
ret = c_div(ln_c, cln_2);
comfree(ln_c);
return ret;
}
/* /*
* Calculate the complex cosine within the specified accuracy. * Calculate the complex cosine within the specified accuracy.
* This uses the formula: * This uses the formula:

53
func.c
View File

@@ -2192,6 +2192,55 @@ f_log(int count, VALUE **vals)
} }
S_FUNC VALUE
f_log2(int count, VALUE **vals)
{
VALUE result;
COMPLEX ctmp, *c;
NUMBER *err;
/* initialize VALUE */
result.v_subtype = V_NOSUBTYPE;
err = conf->epsilon;
if (count == 2) {
if (vals[1]->v_type != V_NUM)
return error_value(E_LOG2_1);
err = vals[1]->v_num;
}
switch (vals[0]->v_type) {
case V_NUM:
if (!qisneg(vals[0]->v_num) &&
!qiszero(vals[0]->v_num)) {
result.v_num = qlog2(vals[0]->v_num, err);
result.v_type = V_NUM;
return result;
}
ctmp.real = vals[0]->v_num;
ctmp.imag = qlink(&_qzero_);
ctmp.links = 1;
c = c_log2(&ctmp, err);
break;
case V_COM:
c = c_log2(vals[0]->v_com, err);
break;
default:
return error_value(E_LOG2_2);
}
if (c == NULL) {
return error_value(E_LOG2_3);
}
result.v_type = V_COM;
result.v_com = c;
if (cisreal(c)) {
result.v_num = qlink(c->real);
result.v_type = V_NUM;
comfree(c);
}
return result;
}
S_FUNC VALUE S_FUNC VALUE
f_cos(int count, VALUE **vals) f_cos(int count, VALUE **vals)
{ {
@@ -10165,10 +10214,8 @@ STATIC CONST struct builtin builtins[] = {
"hypotenuse of right triangle within accuracy c"}, "hypotenuse of right triangle within accuracy c"},
{"ilog", 2, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_2 = f_ilog}, {"ilog", 2, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_2 = f_ilog},
"integral log of a to integral base b"}, "integral log of a to integral base b"},
#if 0 /* XXX - to be added later */
{"ilogn", 2, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_2 = f_ilog}, {"ilogn", 2, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_2 = f_ilog},
"same is ilog"}, "same is ilog"},
#endif /* XXX */
{"ilog10", 1, 1, 0, OP_NOP, {.null = NULL}, {.valfunc_1 = f_ilog10}, {"ilog10", 1, 1, 0, OP_NOP, {.null = NULL}, {.valfunc_1 = f_ilog10},
"integral log of a number base 10"}, "integral log of a number base 10"},
{"ilog2", 1, 1, 0, OP_NOP, {.null = NULL}, {.valfunc_1 = f_ilog2}, {"ilog2", 1, 1, 0, OP_NOP, {.null = NULL}, {.valfunc_1 = f_ilog2},
@@ -10268,6 +10315,8 @@ STATIC CONST struct builtin builtins[] = {
"natural logarithm of value a within accuracy b"}, "natural logarithm of value a within accuracy b"},
{"log", 1, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_cnt = f_log}, {"log", 1, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_cnt = f_log},
"base 10 logarithm of value a within accuracy b"}, "base 10 logarithm of value a within accuracy b"},
{"log2", 1, 2, 0, OP_NOP, {.null = NULL}, {.valfunc_cnt = f_log2},
"base 2 logarithm of value a within accuracy b"},
{"lowbit", 1, 1, 0, OP_LOWBIT, {.null = NULL}, {.null = NULL}, {"lowbit", 1, 1, 0, OP_LOWBIT, {.null = NULL}, {.null = NULL},
"low bit number in base 2 representation"}, "low bit number in base 2 representation"},
{"ltol", 1, 2, FE, OP_NOP, {.numfunc_2 = f_legtoleg}, {.null = NULL}, {"ltol", 1, 2, FE, OP_NOP, {.numfunc_2 = f_legtoleg}, {.null = NULL},

View File

@@ -14,21 +14,30 @@ DESCRIPTION
Approximate the base 2 logarithm function of x by a multiple of Approximate the base 2 logarithm function of x by a multiple of
epsilon, the error having absolute value less than 0.75 * eps. epsilon, the error having absolute value less than 0.75 * eps.
When x is an integer power of 2, log2(x) will return an integer
regardless of the value of eps or epsilon().
If y is a positive integer, log(x, 2^-y) will usually be correct If y is a positive integer, log(x, 2^-y) will usually be correct
to the y-th decimal place. to the y-th decimal place.
When x if a power of 2, log2(x) will return an integer regardless
of the value of eps or epsilon().
EXAMPLE EXAMPLE
; print log2(2), log2(4), log2(1024), log2(2^500) ; print log2(2), log2(4), log2(1024), log2(2^500), log2(1/2^23209)
1 2 10 500 1 2 10 500 -23209
; print log(127), log(23209), log(2^17-19) ; print log2(127), log2(23209), log2(2^17-19)
~6.98868468677216585326 ~14.50239674255407864468 ~16.99979085393743521984 6.98868468677216585326 14.50239674255407864468 16.99979085393743521984
; print log(2+3i, 1e-5) ; print log2(-127)
~1.85020558320709803073+~1.41786049195700786266i 6.98868468677216585326+4.53236014182719380961i
; print log2(2+3i), log2(-2+3i)
1.85021985907054608020+1.41787163074572199658i 1.85021985907054608020+3.11448851108147181304i
; print log2(2-3i), log2(-2-3i)
1.85021985907054608020-1.41787163074572199658i 1.85021985907054608020-3.11448851108147181304i
; print log2(17+0.3i, 1e-75), log2(-17-0.3i, 1e-75)
4.08768744737521449955+0.02545668190826163773i 4.08768744737521449955-4.50690345991893217190i
LIMITS LIMITS
x != 0 x != 0

View File

@@ -1,7 +1,7 @@
/* /*
* qmath - extended precision rational arithmetic primitive routines * qmath - extended precision rational arithmetic primitive routines
* *
* Copyright (C) 1999-2007,2014,2021-2023 David I. Bell and Ernest Bowen * Copyright (C) 1999-2007,2014,2021-2023 David I. Bell, Landon Curt Noll and Ernest Bowen
* *
* Primary author: David I. Bell * Primary author: David I. Bell
* *

View File

@@ -1,7 +1,7 @@
/* /*
* qmath - declarations for extended precision rational arithmetic * qmath - declarations for extended precision rational arithmetic
* *
* Copyright (C) 1999-2007,2014,2021 David I. Bell * Copyright (C) 1999-2007,2014,2021,2023 David I. Bell and Landon Curt Noll
* *
* Calc is open software; you can redistribute it and/or modify it under * 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 * the terms of the version 2.1 of the GNU Lesser General Public License
@@ -185,6 +185,7 @@ E_FUNC NUMBER *qsin(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qexp(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qexp(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qln(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qln(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qlog(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qlog(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qlog2(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qtan(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qtan(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qsec(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qsec(NUMBER *q, NUMBER *epsilon);
E_FUNC NUMBER *qcot(NUMBER *q, NUMBER *epsilon); E_FUNC NUMBER *qcot(NUMBER *q, NUMBER *epsilon);
@@ -248,12 +249,12 @@ E_FUNC NUMBER *swap_HALF_in_NUMBER(NUMBER *dest, NUMBER *src, bool all);
#define qistwo(q) (zistwo((q)->num) && zisunit((q)->den)) #define qistwo(q) (zistwo((q)->num) && zisunit((q)->den))
#define qiseven(q) (zisunit((q)->den) && ziseven((q)->num)) #define qiseven(q) (zisunit((q)->den) && ziseven((q)->num))
#define qisodd(q) (zisunit((q)->den) && zisodd((q)->num)) #define qisodd(q) (zisunit((q)->den) && zisodd((q)->num))
#define qistwopower(q) (zisunit((q)->den) && zistwopower((q)->num))
#define qistiny(q) (zistiny((q)->num)) #define qistiny(q) (zistiny((q)->num))
#define qhighbit(q) (zhighbit((q)->num)) #define qhighbit(q) (zhighbit((q)->num))
#define qlowbit(q) (zlowbit((q)->num)) #define qlowbit(q) (zlowbit((q)->num))
#define qdivcount(q1, q2) (zdivcount((q1)->num, (q2)->num)) #define qdivcount(q1, q2) (zdivcount((q1)->num, (q2)->num))
#define qisreciprocal(q) (zisunit((q)->num) && !ziszero((q)->den))
/* operation on #q may be undefined, so replace with an inline-function */ /* operation on #q may be undefined, so replace with an inline-function */
/* was: #define qlink(q) ((q)->links++, (q)) */ /* was: #define qlink(q) ((q)->links++, (q)) */
static inline NUMBER* qlink(NUMBER* q) { if(q) { (q)->links++; } return q; } static inline NUMBER* qlink(NUMBER* q) { if(q) { (q)->links++; } return q; }

144
qtrans.c
View File

@@ -1,7 +1,7 @@
/* /*
* qtrans - transcendental functions for real numbers * qtrans - transcendental functions for real numbers
* *
* Copyright (C) 1999-2007,2021-2023 David I. Bell and Ernest Bowen * Copyright (C) 1999-2007,2021-2023 David I. Bell, Landon Curt Noll and Ernest Bowen
* *
* Primary author: David I. Bell * Primary author: David I. Bell
* *
@@ -43,10 +43,12 @@ HALF _qlgeden_[] = { 25469 };
NUMBER _qlge_ = { { _qlgenum_, 1, 0 }, { _qlgeden_, 1, 0 }, 1, NULL }; NUMBER _qlge_ = { { _qlgenum_, 1, 0 }, { _qlgeden_, 1, 0 }, 1, NULL };
/* /*
* cache the natural logarithm of 10 * cache the natural logarithm of 10 and 2
*/ */
STATIC NUMBER *ln_10 = NULL; STATIC NUMBER *ln_10 = NULL;
STATIC NUMBER *ln_10_epsilon = NULL; STATIC NUMBER *ln_10_epsilon = NULL;
STATIC NUMBER *ln_2 = NULL;
STATIC NUMBER *ln_2_epsilon = NULL;
/* /*
* cache pi * cache pi
@@ -1184,7 +1186,7 @@ qlog(NUMBER *q, NUMBER *epsilon)
* compute ln(c) first * compute ln(c) first
*/ */
ln_q = qln(q, epsilon); ln_q = qln(q, epsilon);
/* log(1) == 0 */ /* quick return for log(1) == 0 */
if (qiszero(ln_q)) { if (qiszero(ln_q)) {
return ln_q; return ln_q;
} }
@@ -1223,6 +1225,142 @@ qlog(NUMBER *q, NUMBER *epsilon)
} }
/*
* Calculate the base 2 logarithm
*
* log(q) = ln(q) / ln(2)
*/
NUMBER *
qlog2(NUMBER *q, NUMBER *epsilon)
{
int need_new_ln_2 = true; /* false => use cached ln_2 value */
NUMBER *ln_q; /* ln(x) */
NUMBER *ret; /* base 2 logarithm of x */
/* firewall */
if (qiszero(q) || qiszero(epsilon)) {
math_error("logarithm of 0");
not_reached();
}
/*
* special case: q is integer power of 2
*
* When q is integer power of 2, the base 2 logarithm is an integer.
* We return a base 2 logarithm that is in integer in this case.
*
* From above we know that q != 0, so we need to check that q>0.
*
* We have two cases for a power of two: a positive power of 2, and a
* negative power of 2 (i.e., 1 over a power of 2).
*/
if (qispos(q)) {
ZVALUE zlog2; /* base 2 logarithm when q is power of 2 */
/*
* case: q>0 is an integer
*/
if (qisint(q)) {
/*
* check if q is an integer power of 2
*/
if (zispowerof2(q->num, &zlog2)) {
/*
* case: q>0 is an integer power of 2
*
* Return zlog2, which is an integer power of 2 as a NUMBER.
*/
ret = qalloc();
zcopy(zlog2, &ret->num);
zfree(zlog2);
return ret;
/*
* case: integer q is not an integer power of 2
*/
} else {
/* free the zispowerof2() 2nd arg and proceed to calculate ln(q)/ln(2) */
zfree(zlog2);
}
/*
* case: q>0 is 1 over an integer
*/
} else if (qisreciprocal(q)) {
/*
* check if q is 1 over an integer power of 2
*/
if (zispowerof2(q->den, &zlog2)) {
/*
* case: q>0 is an integer power of 2
*
* Return zlog2, which is an negative integer power of 2 as a NUMBER.
*/
ret = qalloc();
zlog2.sign = !zlog2.sign; /* zlog2 = -zlog2 */
zcopy(zlog2, &ret->num);
zfree(zlog2);
return ret;
/*
* case: reciprocal q is not an integer power of 2
*/
} else {
/* free the zispowerof2() 2nd arg and proceed to calculate ln(q)/ln(2) */
zfree(zlog2);
}
}
}
/*
* compute ln(c) first
*/
ln_q = qln(q, epsilon);
/* quick return for log(1) == 0 */
if (qiszero(ln_q)) {
return ln_q;
}
/*
* save epsilon for ln(2) if needed
*/
if (ln_2_epsilon == NULL) {
/* first time call */
ln_2_epsilon = qcopy(epsilon);
} else if (qcmp(ln_2_epsilon, epsilon) == true) {
/* replaced cached value with epsilon arg */
qfree(ln_2_epsilon);
ln_2_epsilon = qcopy(epsilon);
} else if (ln_2 != NULL) {
/* the previously computed ln(2) is OK to use */
need_new_ln_2 = false;
}
/*
* compute ln(2) if needed
*/
if (need_new_ln_2 == true) {
if (ln_2 != NULL) {
qfree(ln_2);
}
ln_2 = qln(&_qtwo_, ln_2_epsilon);
}
/*
* return ln(q) / ln(2)
*/
ret = qqdiv(ln_q, ln_2);
qfree(ln_q);
return ret;
}
/* /*
* Calculate the result of raising one number to the power of another. * Calculate the result of raising one number to the power of another.
* The result is calculated to the nearest or next to nearest multiple of * The result is calculated to the nearest or next to nearest multiple of

View File

@@ -3,7 +3,7 @@
* *
* Copyright (C) 1999-2023 David I. Bell and Landon Curt Noll * Copyright (C) 1999-2023 David I. Bell and Landon Curt Noll
* *
* Primary author: David I. Bell * See "version.h" for the actual calc version constants.
* *
* Calc is open software; you can redistribute it and/or modify it under * 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 * the terms of the version 2.1 of the GNU Lesser General Public License

78
zfunc.c
View File

@@ -2236,3 +2236,81 @@ zissquare(ZVALUE z)
zfree(tmp); zfree(tmp);
return (n ? true : false); return (n ? true : false);
} }
/*
* test if a number is an integer power of 2
*
* given:
* z value to check if it is a power of 2
* zlog2 >= 0 ==> *zlog2 power of 2 of z (return true)
* < 0 z is not a power of 2 (return false)
*
* returns:
* true z is a power of 2
* false z is not a power of 2
*/
bool
zispowerof2(ZVALUE z, ZVALUE *zlog2)
{
FULL ilogz=0; /* potential log base 2 return value or -1 */
HALF tophalf; /* most significant HALF in z */
LEN len; /* length of z in HALFs */
int i;
/* firewall */
if (zlog2 == NULL) {
math_error("%s: zlog2 NULL", __func__);
not_reached();
}
/* zero and negative values are never integer powers of 2 */
if (ziszero(z) || zisneg(z)) {
*zlog2 = _neg_one_;
return false;
}
/*
* trim z just in case
*
* An untrimmed z will give incorrect results.
*/
ztrim(&z);
/*
* all HALFs below the top HALF must be zero
*/
len = z.len;
for (i=0, ilogz=0; i < len-1; ++i, ilogz+=BASEB) {
if (z.v[i] != 0) {
*zlog2 = _neg_one_;
return false;
}
}
/*
* top HALF must be a power of 2
*
* For non-zero values of tophalf,
* (tophalf & (tophalf-1)) == 0 ==> tophalf is a power of 2,
* (tophalf & (tophalf-1)) != 0 ==> tophalf is NOT a power of 2.
*/
tophalf = z.v[len-1];
if ((tophalf == 0) || ((tophalf & (tophalf-1)) != 0)) {
*zlog2 = _neg_one_;
return false;
}
/*
* count the bits in the top HALF which is a power of 2
*/
for (i=0; i < BASEB && tophalf != ((HALF)1 << i); ++i) {
++ilogz;
}
/*
* return power of 2
*/
utoz(ilogz, zlog2);
return true;
}

33
zmath.c
View File

@@ -1,7 +1,7 @@
/* /*
* zmath - extended precision integral arithmetic primitives * zmath - extended precision integral arithmetic primitives
* *
* Copyright (C) 1999-2007,2021-2023 David I. Bell and Ernest Bowen * Copyright (C) 1999-2007,2021-2023 David I. Bell, Landon Curt Noll and Ernest Bowen
* *
* Primary author: David I. Bell * Primary author: David I. Bell
* *
@@ -2163,34 +2163,3 @@ zpopcnt(ZVALUE z, int bitval)
*/ */
return cnt; return cnt;
} }
#if 0 /* XXX - to be added later */
/*
* test if a number is a power of 2
*
* given:
* z value to check if it is a power of 2
* zlog2 set to log base 2 of z if z is a power of 2, 0 otherwise
*
* returns:
* true z is a power of 2
* false z is not a power of 2
*/
bool
zispowerof2(ZVALUE z, ZVALUE *zlog2)
{
/* firewall */
if (zlog2 == NULL) {
math_error("%s: zlog2 NULL", __func__);
not_reached();
}
/* zero and negative values are never powers of 2 */
if (ziszero(z) || zisneg(z)) {
return false;
}
/* XXX - add code here - XXX */
}
#endif /* XXX */

View File

@@ -1,7 +1,7 @@
/* /*
* zmath - declarations for extended precision integer arithmetic * zmath - declarations for extended precision integer arithmetic
* *
* Copyright (C) 1999-2007,2014,2021,2023 David I. Bell * Copyright (C) 1999-2007,2014,2021,2023 David I. Bell and Landon Curt Noll
* *
* Calc is open software; you can redistribute it and/or modify it under * 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 * the terms of the version 2.1 of the GNU Lesser General Public License
@@ -415,6 +415,7 @@ E_FUNC FLAG zsqrt(ZVALUE z1, ZVALUE *dest, long R);
E_FUNC void zroot(ZVALUE z1, ZVALUE z2, ZVALUE *dest); E_FUNC void zroot(ZVALUE z1, ZVALUE z2, ZVALUE *dest);
E_FUNC bool zissquare(ZVALUE z); E_FUNC bool zissquare(ZVALUE z);
E_FUNC void zhnrmod(ZVALUE v, ZVALUE h, ZVALUE zn, ZVALUE zr, ZVALUE *res); E_FUNC void zhnrmod(ZVALUE v, ZVALUE h, ZVALUE zn, ZVALUE zr, ZVALUE *res);
E_FUNC bool zispowerof2(ZVALUE z, ZVALUE *zlog2);
/* /*