[gnumeric] ILOG: new function.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] ILOG: new function.
- Date: Wed, 13 Oct 2021 00:43:40 +0000 (UTC)
commit 0bada92137f14ebb4ece553a595c3f331613db26
Author: Morten Welinder <terra gnome org>
Date: Tue Oct 12 20:41:39 2021 -0400
ILOG: new function.
This is the integer log function. Implemented for bases 2 and 10 so far.
NEWS | 1 +
plugins/fn-math/functions.c | 34 ++++++++++++++++++++++
plugins/fn-math/plugin.xml.in | 1 +
src/mathfunc.c | 67 +++++++++++++++++++++++++++++++++++++++++++
src/mathfunc.h | 1 +
5 files changed, 104 insertions(+)
---
diff --git a/NEWS b/NEWS
index 8ab05ab52..774038ad4 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@ Morten:
* Fix --with-long-double configuration.
* Fix applix import/locale problem.
* Make sure CEIL doesn't return -0. [#603]
+ * Add ILOG function. [#591]
--------------------------------------------------------------------------
Gnumeric 1.12.50
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index 1726d6846..20f6d2ce8 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -1339,6 +1339,37 @@ gnumeric_log (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
/***************************************************************************/
+static GnmFuncHelp const help_ilog[] = {
+ { GNM_FUNC_HELP_NAME, F_("ILOG:integer logarithm of @{x} with base @{base}")},
+ { GNM_FUNC_HELP_ARG, F_("x:positive number")},
+ { GNM_FUNC_HELP_ARG, F_("base:base of the logarithm, defaults to 10")},
+ { GNM_FUNC_HELP_NOTE, F_("@{base} must be positive and not equal to 1.") },
+ { GNM_FUNC_HELP_NOTE, F_("If @{x} \xe2\x89\xa4 0, LOG returns #NUM! error.") },
+ { GNM_FUNC_HELP_NOTE, F_("This function returns the logarithm of @{x} using @{base} rounded down to
nearest integer. Unlike FLOOR(LOG(@{x},@{base})), this function is not subject error of representation of
the intermediate result.") },
+ { GNM_FUNC_HELP_NOTE, F_("This function is not implemented for all possible value. #VALUE! will be
returned for such arguments.") },
+ { GNM_FUNC_HELP_EXAMPLES, "=ILOG(2^32,2)" },
+ { GNM_FUNC_HELP_EXAMPLES, "=LOG(10^15)" },
+ { GNM_FUNC_HELP_SEEALSO, "LOG"},
+ { GNM_FUNC_HELP_END}
+};
+
+static GnmValue *
+gnumeric_ilog (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+{
+ gnm_float x = value_get_as_float (argv[0]);
+ gnm_float base = argv[1] ? value_get_as_float (argv[1]) : 10;
+
+ if (base == 1. || base <= 0.)
+ return value_new_error_NUM (ei->pos);
+
+ if (x <= 0.0)
+ return value_new_error_NUM (ei->pos);
+
+ return value_new_float (gnm_ilog (x, base));
+}
+
+/***************************************************************************/
+
static GnmFuncHelp const help_ln[] = {
{ GNM_FUNC_HELP_NAME, F_("LN:the natural logarithm of @{x}")},
{ GNM_FUNC_HELP_ARG, F_("x:positive number")},
@@ -3619,6 +3650,9 @@ GnmFuncDescriptor const math_functions[] = {
{ "igamma", "ff|bbb", help_igamma,
gnumeric_igamma, NULL,
GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
+ { "ilog", "f|f", help_ilog,
+ gnumeric_ilog, NULL,
+ GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
{ "int", "f", help_int,
gnumeric_int, NULL,
GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST,
diff --git a/plugins/fn-math/plugin.xml.in b/plugins/fn-math/plugin.xml.in
index b5bd4bd8b..70b221097 100644
--- a/plugins/fn-math/plugin.xml.in
+++ b/plugins/fn-math/plugin.xml.in
@@ -59,6 +59,7 @@
<function name="gd"/>
<function name="hypot"/>
<function name="igamma"/>
+ <function name="ilog"/>
<function name="int"/>
<function name="lambertw"/>
<function name="lcm"/>
diff --git a/src/mathfunc.c b/src/mathfunc.c
index f51b72261..c2222ec14 100644
--- a/src/mathfunc.c
+++ b/src/mathfunc.c
@@ -6041,3 +6041,70 @@ gnm_owent (gnm_float h, gnm_float a)
}
/* ------------------------------------------------------------------------- */
+
+gnm_float
+gnm_ilog (gnm_float x, gnm_float b)
+{
+ if (gnm_isnan (x) || x < 0 ||
+ gnm_isnan (b) || b == 1 || b <= 0 || b == gnm_pinf)
+ return gnm_nan;
+
+ if (x == 0)
+ return b < 1 ? gnm_pinf : gnm_ninf;
+
+ if (x == gnm_pinf)
+ return b < 1 ? gnm_ninf : gnm_pinf;
+
+ if (b == 2) {
+ int e;
+ gnm_float m = gnm_frexp (x, &e);
+ (void)m;
+ return e - 1;
+ }
+
+ if (b == 10) {
+ if (x >= 1 && x <= 1e22) {
+ // This code relies on 10^i being exact
+ int l10 = (int)(gnm_log10 (x));
+ if (gnm_pow10 (l10) > x)
+ l10++;
+ return l10;
+ } else {
+ void *state = gnm_quad_start ();
+ GnmQuad qx, q10, qlog10, qfudge;
+
+ gnm_quad_init (&q10, 10);
+ gnm_quad_log (&qlog10, &q10);
+
+ gnm_quad_init (&qx, x);
+ gnm_quad_log (&qx, &qx);
+ gnm_quad_div (&qx, &qx, &qlog10);
+
+ // This looks bad, but actually isn't because the
+ // true logarithm cannot be too close to an integer
+ // while still being less.
+ //
+ // Let eps = 1ulp for 10^i (roughly 10^i * GNM_EPSILON)
+ //
+ // log10(10^i-eps) =
+ // i + log10(1-eps/10^i) =
+ // 1 - eps/10^i/log(10) + O((eps/10^i)^2)
+ // As long as we add something smaller than
+ // eps/10^i/log(10) (roughly GNM_EPSILON/3), we
+ // should be fine. *should*
+ // Verification needed.
+ gnm_quad_init (&qfudge, GNM_EPSILON / 32);
+ gnm_quad_add (&qx, &qx, &qfudge);
+ gnm_quad_floor (&qx, &qx);
+
+ gnm_quad_end (state);
+
+ return gnm_quad_value (&qx);
+ }
+ }
+
+ // Not implemented.
+ return gnm_nan;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/src/mathfunc.h b/src/mathfunc.h
index 997c8ba7d..cbdf32cf2 100644
--- a/src/mathfunc.h
+++ b/src/mathfunc.h
@@ -39,6 +39,7 @@ gnm_float gnm_logcf (gnm_float x, gnm_float i, gnm_float d);
gnm_float expmx2h (gnm_float x);
gnm_float gnm_agm(gnm_float a, gnm_float b);
gnm_float gnm_lambert_w(gnm_float x, int k);
+gnm_float gnm_ilog (gnm_float x, gnm_float b);
/* "d": density. */
/* "p": distribution function. */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]