From 41931fd90880e5bcca47b828e460445f88a88469 Mon Sep 17 00:00:00 2001 From: Keith Marshall Date: Tue, 8 Nov 2016 14:11:39 +0000 Subject: [PATCH] Implement unit tests for logarithmic functions. --- mingwrt/ChangeLog | 11 ++ mingwrt/tests/Makefile.in | 2 +- mingwrt/tests/logarithms.at | 329 ++++++++++++++++++++++++++++++++++++++++++ mingwrt/tests/testsuite.at.in | 1 + 4 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 mingwrt/tests/logarithms.at diff --git a/mingwrt/ChangeLog b/mingwrt/ChangeLog index 520ed70..afd0b85 100644 --- a/mingwrt/ChangeLog +++ b/mingwrt/ChangeLog @@ -1,3 +1,14 @@ +2016-11-08 Keith Marshall + + Implement unit tests for logarithmic functions. + + * tests/testsuite.at.in: m4_include... + * tests/Makefile.in (testsuite): ...and add dependency on... + * tests/logarithms.at: ...this new file; it specifies unit tests to + check POSIX.1-2008 compliance of results and errno assignment from... + (log, logf, logl, log1p, log1pf, log1pl, log10, log10f, log10l, log2) + (log2f, log2l): ...each of these functions. + 2016-11-02 Keith Marshall Implement unit tests for ANSI printf() functions. diff --git a/mingwrt/tests/Makefile.in b/mingwrt/tests/Makefile.in index dcae350..9de9826 100644 --- a/mingwrt/tests/Makefile.in +++ b/mingwrt/tests/Makefile.in @@ -102,7 +102,7 @@ autotest_missing = $(call missing,autom4te,Unable to compile the testsuite) testsuite: %: %.at $(AUTOTEST_COMPILE) -o $* $< -testsuite: headers.at ansiprintf.at +testsuite: headers.at ansiprintf.at logarithms.at # Display a diagnostic message, explaining that any specified program # is required, but has not been installed. diff --git a/mingwrt/tests/logarithms.at b/mingwrt/tests/logarithms.at new file mode 100644 index 0000000..a1b431b --- /dev/null +++ b/mingwrt/tests/logarithms.at @@ -0,0 +1,329 @@ +# logarithms.at +# +# Autotest module to verify correct operation of the log(), logf(), +# logl(), log10(), log10f(), log10l(), log1p(), log1pf(), log1pl(), +# log2(), log2f(), and log2l() functions, with respect to each of +# the boundary conditions specified by POSIX.1-2008. +# +# $Id$ +# +# Written by Keith Marshall +# Copyright (C) 2016, MinGW.org Project +# +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# +# All tests specified herein are written in the C language. +# +MINGW_AT_LANG([C]) + +# MINGW_AT_CHECK_RUN_LOG( FUNCTION, X ) +# ------------------------------------- +# Set up the test case to evaluate FUNCTION, (one of log, logf, +# logl, log10, log10f, log10l, log1p, log1pf, log1pl, log2, log2f, +# or log2l), with X as argument; confirm the the resultant output +# matches that specified by _FUNCTION_expout(X). +# +# Note: the M4 macros log_datatype, log_strtod, and log_format +# MUST have been defined, before this macro is expanded. +# +m4_define([MINGW_AT_CHECK_RUN_LOG],[ +AT_SETUP([$1 (x = $2)])AT_KEYWORDS([C logarithms $1]) +MINGW_AT_DATA_CRLF([expout],[_$1_expout([$2]) +])MINGW_AT_CHECK_RUN([[[ +#define _XOPEN_SOURCE 700 + +#include +#include +#include +#include + +#define EOK 0 + +const char *errdesc( void ) +{ /* A minimal replacement for strerror(), so we keep strict + * control of the descriptions for those errors which any of + * the log() functions may report. + */ + static char desc[8] = ""; + switch( errno ) + { case EOK: return "ok"; + case EDOM: return "domain error"; + case ERANGE: return "range error"; + default: snprintf( desc, sizeof( desc ), "%d", errno ); + } + return desc; +} + +int main() +{ /* Convert the specified X argument to the appropriate data type, + * invoke the specified FUNCTION, report its result, and any error + * which may have occurred. + */ + ]]log_datatype[[ result, x = ]]log_strtod($2)[[; errno = EOK; + result = $1(x); printf("]]log_format[[", result, errdesc()); + return EOK; +}]]],,[expout])dnl +AT_CLEANUP +]) + +# MINGW_AT_CHECK_LOG_FUNCTION( NAME ) +# ----------------------------------- +# Check the behaviour of function NAME, (one of log, logf, or logl, +# log1p, log1pf, or log1pl, log10, log10f, or log10l, log2, log2f or +# log2l), for a selection of values of X. +# +m4_define([MINGW_AT_CHECK_LOG_FUNCTION],[dnl +AT_BANNER([Logarithmic function $1() tests: ]log_datatype[ data.]) +# +# On successful completion, return log(x) with the appropriate base. +# +MINGW_AT_CHECK_RUN_LOG([$1],[-1.30000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[-1.00000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[-0.30000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[-0.29000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[-0.28000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[-0.00001e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.00000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.00001e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.28000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.29000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.30000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.70000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.71000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.72000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+0.99999e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.00000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.00001e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.28000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.29000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.30000e+000]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.28000e+038]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.28000e+308]) +MINGW_AT_CHECK_RUN_LOG([$1],[+1.28000e+999]) +MINGW_AT_CHECK_RUN_LOG([$1],[-inf]) +]) + +# _log_expout( X ) +# ---------------- +# Define expected outcomes for log(X), logf(X), and logl(X), given +# a specific input value of X; these represent the Naperian (base-e) +# logarithms for the specified X values. +# +m4_define([_log_expout],[m4_if(dnl +[$1],[+1.28000e+999],[+inf (ok)], +[_logl_expout([$1])])]) +m4_define([_logf_expout],[m4_if(dnl +dnl All of the conventional log(X) algorithms suffer from loss of +dnl precision in the region where X --> 1.0; this is particularly +dnl evident in the case of the REAL4 (float) implementations, which +dnl deviate from the expected value (--> 0.00000) after only three +dnl significant digits. +[$1],[+0.99999e+000],[-1.00136e-005 (ok)],dnl expected loss of precision... +[$1],[+1.00001e+000],[+1.00135e-005 (ok)],dnl for values of X near 1.0 +[$1],[+1.28000e+308],[+inf (ok)], +[_log_expout([$1])])]) +m4_define([_logl_expout],[m4_if(dnl +[$1],[+0.00001e+000],[-1.15129e+001 (ok)], +[$1],[+0.28000e+000],[-1.27297e+000 (ok)], +[$1],[+0.29000e+000],[-1.23787e+000 (ok)], +[$1],[+0.30000e+000],[-1.20397e+000 (ok)], +[$1],[+0.70000e+000],[-3.56675e-001 (ok)], +[$1],[+0.71000e+000],[-3.42490e-001 (ok)], +[$1],[+0.72000e+000],[-3.28504e-001 (ok)], +[$1],[+0.99999e+000],[-1.00001e-005 (ok)], +[$1],[+1.00001e+000],[+9.99995e-006 (ok)], +[$1],[+1.28000e+000],[+2.46860e-001 (ok)], +[$1],[+1.29000e+000],[+2.54642e-001 (ok)], +[$1],[+1.30000e+000],[+2.62364e-001 (ok)], +[_logx_expout([$1])])]) + +# _log10_expout( X ) +# ------------------ +# Define expected outcomes for log10(X), log10f(X), and log10l(X), +# given any specific input value of X; these represent the base-10 +# logarithms for the specified X values. +# +m4_define([_log10_expout],[m4_if(dnl +[$1],[+1.28000e+999],[+inf (ok)], +[_log10l_expout([$1])])]) +m4_define([_log10f_expout],[m4_if(dnl +dnl As in the logf(X) case, log10f(X) shows marked deviation from +dnl expectation as X --> 1.0; once again, we observe deviation after +dnl only three significant digits, at |X-1.0| == 1.0e-5, for this +dnl REAL4 (float) implementation, (with additional deviation in +dnl the sixth significant digit, around |X-1.0| --> 0.28). +[$1],[+0.72000e+000],[-1.42667e-001 (ok)],dnl last digit difference +[$1],[+0.99999e+000],[-4.34886e-006 (ok)],dnl expected loss of precision... +[$1],[+1.00001e+000],[+4.34882e-006 (ok)],dnl for values of X near 1.0 +[$1],[+1.28000e+308],[+inf (ok)], +[_log10_expout([$1])])]) +m4_define([_log10l_expout],[m4_if(dnl +[$1],[+0.00001e+000],[-5.00000e+000 (ok)], +[$1],[+0.28000e+000],[-5.52842e-001 (ok)], +[$1],[+0.29000e+000],[-5.37602e-001 (ok)], +[$1],[+0.30000e+000],[-5.22879e-001 (ok)], +[$1],[+0.70000e+000],[-1.54902e-001 (ok)], +[$1],[+0.71000e+000],[-1.48742e-001 (ok)], +[$1],[+0.72000e+000],[-1.42668e-001 (ok)], +[$1],[+0.99999e+000],[-4.34297e-006 (ok)], +[$1],[+1.00001e+000],[+4.34292e-006 (ok)], +[$1],[+1.28000e+000],[+1.07210e-001 (ok)], +[$1],[+1.29000e+000],[+1.10590e-001 (ok)], +[$1],[+1.30000e+000],[+1.13943e-001 (ok)], +[$1],[+1.28000e+038],[+3.81072e+001 (ok)], +[$1],[+1.28000e+308],[+3.08107e+002 (ok)], +[$1],[+1.28000e+999],[+9.99107e+002 (ok)], +[_logx_expout([$1])])]) + +# _log1p_expout( X ) +# ------------------ +# Define expected outcomes for log1p(X+1), log1pf(X+1), and log1pl(X+1), +# given a specific input value of X; these represent the Naperian (base-e) +# logarithms for the specified X+1 values. +# +m4_define([_log1p_expout],[m4_if(dnl +[$1],[+1.28000e+999],[+inf (ok)], +[_log1pl_expout([$1])])]) +m4_define([_log1pf_expout],[m4_if(dnl +dnl While the log1p(X) algorithm yields much better accuracy in the region +dnl where X --> 0.0, (equivalent to X --> 1.0 for the other algorithms), we +dnl may still observe some loss of precision (in the sixth significant digit +dnl here) for the REAL4 implementation; (since the limit of precision for +dnl REAL4 is no more than seven significant digits anyway, this represents +dnl no more than a rounding effect from the least significant available +dnl digit, so this may be considered reasonable accuracy). +[$1],[-0.00001e+000],[-1.00000e-005 (ok)], +[$1],[+1.28000e+308],[+inf (ok)], +[_log1p_expout([$1])])]) +m4_define([_log1pl_expout],[m4_if(dnl +[$1],[-1.00000e+000],[-inf (range error)], +[$1],[-0.30000e+000],[-3.56675e-001 (ok)], +[$1],[-0.29000e+000],[-3.42490e-001 (ok)], +[$1],[-0.28000e+000],[-3.28504e-001 (ok)], +[$1],[-0.00001e+000],[-1.00001e-005 (ok)], +[$1],[+0.00000e+000],[+0.00000e+000 (ok)], +[$1],[+0.00001e+000],[+9.99995e-006 (ok)], +[$1],[+0.28000e+000],[+2.46860e-001 (ok)], +[$1],[+0.29000e+000],[+2.54642e-001 (ok)], +[$1],[+0.30000e+000],[+2.62364e-001 (ok)], +[$1],[+0.70000e+000],[+5.30628e-001 (ok)], +[$1],[+0.71000e+000],[+5.36493e-001 (ok)], +[$1],[+0.72000e+000],[+5.42324e-001 (ok)], +[$1],[+0.99999e+000],[+6.93142e-001 (ok)], +[$1],[+1.00000e+000],[+6.93147e-001 (ok)], +[$1],[+1.00001e+000],[+6.93152e-001 (ok)], +[$1],[+1.28000e+000],[+8.24175e-001 (ok)], +[$1],[+1.29000e+000],[+8.28552e-001 (ok)], +[$1],[+1.30000e+000],[+8.32909e-001 (ok)], +[_logx_expout([$1])])]) + +# _log2_expout( X ) +# ----------------- +# Define expected outcomes for log(X), logf(X), and logl(X), given +# a specific input value of X; these represent the Naperian (base-e) +# logarithms for the specified X values. +# +m4_define([_log2_expout],[m4_if(dnl +[$1],[+1.28000e+999],[+inf (ok)], +[_log2l_expout([$1])])]) +m4_define([_log2f_expout],[m4_if(dnl +dnl As in the case of other log(X) implementations, the REAL4 (float) +dnl implementation again exhibits severe loss of precision beyond the +dnl third significant digit, in the X --> 1.0 region. +[$1],[+0.99999e+000],[-1.44466e-005 (ok)],dnl bad rounding error here! +[$1],[+1.00001e+000],[+1.44465e-005 (ok)],dnl and here. +[$1],[+1.28000e+308],[+inf (ok)], +[_log2_expout([$1])])]) +m4_define([_log2l_expout],[m4_if(dnl +[$1],[+0.00001e+000],[-1.66096e+001 (ok)], +[$1],[+0.28000e+000],[-1.83650e+000 (ok)], +[$1],[+0.29000e+000],[-1.78588e+000 (ok)], +[$1],[+0.30000e+000],[-1.73697e+000 (ok)], +[$1],[+0.70000e+000],[-5.14573e-001 (ok)], +[$1],[+0.71000e+000],[-4.94109e-001 (ok)], +[$1],[+0.72000e+000],[-4.73931e-001 (ok)], +[$1],[+0.99999e+000],[-1.44270e-005 (ok)], +[$1],[+1.00001e+000],[+1.44269e-005 (ok)], +[$1],[+1.28000e+000],[+3.56144e-001 (ok)], +[$1],[+1.29000e+000],[+3.67371e-001 (ok)], +[$1],[+1.30000e+000],[+3.78512e-001 (ok)], +[$1],[+1.28000e+038],[+1.26589e+002 (ok)], +[$1],[+1.28000e+308],[+1.02351e+003 (ok)], +[$1],[+1.28000e+999],[+3.31896e+003 (ok)], +[_logx_expout([$1])])]) + +# _logx_expout( X ) +# ----------------- +# Define expected outcomes, given a specific input value for X, +# which may be expected to be the same for ALL of the implemented +# logarithm algorithms, (unless otherwise specified for a specific +# algorithm; note that this includes the Naperian logarithm values +# for very large X, which are the same for both log(X) and log1p(X), +# since the difference between X and X+1.0 becomes insignificant at +# such large values of X). +m4_define([_logx_expout],[m4_if(dnl +[$1],[+0.00000e+000],[-inf (range error)], +[$1],[+1.00000e+000],[+0.00000e+000 (ok)], +[$1],[+1.28000e+038],[+8.77451e+001 (ok)], +[$1],[+1.28000e+308],[+7.09443e+002 (ok)], +[$1],[+1.28000e+999],[+2.30053e+003 (ok)], +[+nan (domain error)])]) + + +# Set up the test sequence for each of the log(), log10(), +# log1p(), and log2() functions. +# +m4_define([log_datatype],[double]) +m4_define([log_strtod],[[strtod("$1",NULL)]]) +m4_define([log_format],[[%+.5e (%s)\n]]) +# +MINGW_AT_CHECK_LOG_FUNCTION([log]) +MINGW_AT_CHECK_LOG_FUNCTION([log1p]) +MINGW_AT_CHECK_LOG_FUNCTION([log10]) +MINGW_AT_CHECK_LOG_FUNCTION([log2]) + +# Repeat for each of the logf(), log10f(), log1pf(), +# and log2f() functions. +# +m4_define([log_datatype],[float]) +m4_define([log_strtod],[[strtof("$1",NULL)]]) +m4_define([log_format],[[%+.5e (%s)\n]]) +# +MINGW_AT_CHECK_LOG_FUNCTION([logf]) +MINGW_AT_CHECK_LOG_FUNCTION([log1pf]) +MINGW_AT_CHECK_LOG_FUNCTION([log10f]) +MINGW_AT_CHECK_LOG_FUNCTION([log2f]) + +# And finally, for each of the logl(), log10l(), log1pll(), +# and log2l() functions. +# +m4_define([log_datatype],[long double]) +m4_define([log_strtod],[[strtold("$1",NULL)]]) +m4_define([log_format],[[%+.5Le (%s)\n]]) +# +MINGW_AT_CHECK_LOG_FUNCTION([logl]) +MINGW_AT_CHECK_LOG_FUNCTION([log1pl]) +MINGW_AT_CHECK_LOG_FUNCTION([log10l]) +MINGW_AT_CHECK_LOG_FUNCTION([log2l]) + +# vim: filetype=config formatoptions=croql +# $RCSfile$: end of file diff --git a/mingwrt/tests/testsuite.at.in b/mingwrt/tests/testsuite.at.in index 3e565e4..3252925 100644 --- a/mingwrt/tests/testsuite.at.in +++ b/mingwrt/tests/testsuite.at.in @@ -88,6 +88,7 @@ MINGW_AT_LANG([C]) # m4_include([headers.at]) m4_include([ansiprintf.at]) +m4_include([logarithms.at]) # vim: filetype=config formatoptions=croql # $RCSfile$: end of file -- 2.11.0