OSDN Git Service

Implement unit tests for power functions.
authorKeith Marshall <keithmarshall@users.sourceforge.net>
Fri, 18 Nov 2016 09:34:08 +0000 (09:34 +0000)
committerKeith Marshall <keithmarshall@users.sourceforge.net>
Fri, 18 Nov 2016 09:34:08 +0000 (09:34 +0000)
mingwrt/ChangeLog
mingwrt/tests/Makefile.in
mingwrt/tests/powerfunc.at [new file with mode: 0644]
mingwrt/tests/testsuite.at.in

index 2ae49ab..b5ea2ae 100644 (file)
@@ -1,3 +1,13 @@
+2016-11-18  Keith Marshall  <keithmarshall@users.sourceforge.net>
+
+       Implement unit tests for power functions.
+
+       * tests/testsuite.at.in: m4_include...
+       * tests/Makefile.in (testsuite): ...and add dependency on...
+       * tests/powerfunc.at: ...this new file; it specifies unit tests to
+       check POSIX.1-2008 compliance of results and errno assignment from...
+       (pow, powf, powl): ...each of these functions.
+
 2016-11-14  Keith Marshall  <keithmarshall@users.sourceforge.net>
 
        Add POSIX compliant errno assignments to log functions.
index 9de9826..1a57f37 100644 (file)
@@ -102,7 +102,7 @@ autotest_missing = $(call missing,autom4te,Unable to compile the testsuite)
 testsuite: %: %.at
        $(AUTOTEST_COMPILE) -o $* $<
 
-testsuite: headers.at ansiprintf.at logarithms.at
+testsuite: headers.at ansiprintf.at logarithms.at powerfunc.at
 
 # Display a diagnostic message, explaining that any specified program
 # is required, but has not been installed.
diff --git a/mingwrt/tests/powerfunc.at b/mingwrt/tests/powerfunc.at
new file mode 100644 (file)
index 0000000..e76cd51
--- /dev/null
@@ -0,0 +1,274 @@
+# powerfunc.at
+#
+# Autotest module to verify correct operation of the pow(), powf(),
+# and powl() functions; inspired by MinGW-Bug [#2306], and implemented
+# to evaluate each boundary condition specified by POSIX.1-2008.
+#
+# $Id$
+#
+# Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+# 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_POW( FUNCTION, X, Y, EXPOUT )
+# ------------------------------------------------
+# Set up the test case to evaluate FUNCTION, (one of pow, powf,
+# or powl), with X and Y as arguments; confirm the the resultant
+# output matches EXPOUT.
+#
+# Note: the M4 macros pow_datatype, pow_strtod, and pow_format
+# MUST have been defined, before this macro is expanded.
+#
+m4_define([MINGW_AT_CHECK_RUN_POW],[
+AT_SETUP([$1 (x = $2, y = $3)])
+AT_KEYWORDS([C $1])MINGW_AT_DATA_CRLF([expout],[[$4
+]])MINGW_AT_CHECK_RUN([[[
+#define _XOPEN_SOURCE 700
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <tgmath.h>
+#include <errno.h>
+
+#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 pow() 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 and Y arguments to the appropriate
+   * data type, invoke the specified FUNCTION, report its result,
+   * and any error which may have occurred.
+   */
+  ]]pow_datatype[[ result, x = ]]pow_strtod($2)[[, y = ]]pow_strtod($3)[[;
+  errno = EOK; result = $1(x, y); printf("]]pow_format[[", result, errdesc());
+  return EOK;
+}]]],,[expout])dnl
+AT_CLEANUP
+])
+
+# MINGW_AT_CHECK_POW_FUNCTION( NAME )
+# -----------------------------------
+# Check the behaviour of function NAME, (one of pow, powf, or powl),
+# for a selection of values of X and Y; further comments, embedded
+# within this macro, specify POSIX.1-2008 mandatory outcomes.
+#
+m4_define([MINGW_AT_CHECK_POW_FUNCTION],[dnl
+AT_BANNER([Power function tests: ]pow_datatype[ data.])
+#
+# On successful completion, return x to the power y
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+2.0],[+2.5e+0],[+5.656854e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+1.2],[+2.5e+0],[+1.577441e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+0.3],[+2.5e+0],[+4.929503e-002 (ok)])
+#
+# For finite x < 0, and finite non-integral y, report "domain error",
+# and return "NaN"; (note that MinGW's printf(), as used to report the
+# result, always treats NaN as being unsigned, so we will always see
+# it reported as "+nan", irrespective of sign bit).
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-2.0],[+2.5e+0],[+nan (domain error)])
+MINGW_AT_CHECK_RUN_POW([$1],[-2.0],[-2.5e+0],[+nan (domain error)])
+#
+# For finite x < 0, and finite integral y, no "domain error" arises;
+# the result is analytically computable as -1.0^y * 2^(y*log2(|x|)).
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-2.5],[+2.0e+0],[+6.250000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-2.5],[+3.0e+0],[-1.562500e+001 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-1.5],[-2.0e+0],[+4.444444e-001 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-1.5],[-3.0e+0],[-2.962963e-001 (ok)])
+#
+# For non-zero result with no representation which is distinguishable
+# from zero, (i.e. underflow), report "range error" and return zero.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+0.2],[+2.5e+4],[+0.000000e+000 (range error)])
+#
+# When |x| == 0, and y < 0, report "range error", (indicating a pole
+# error), and return infinity with the sign of x if y is an odd valued
+# -ve integer value, or +ve infinity for any other -ve value of y.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+0.0],[-1.0e+0],[+inf (range error)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[-1.0e+0],[-inf (range error)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[-7.0e+0],[-inf (range error)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[-2.0e+0],[+inf (range error)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[-1.1e+0],[+inf (range error)])
+#
+# When x == +1.0, for any value of y (including NaN), return +1.0
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+1.0],[+2.5e+4],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+1.0],[+Inf],   [+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+1.0],[+NaN],   [+1.000000e+000 (ok)])
+#
+# for any value of x, (including infinity or NaN), when |y| is zero,
+# return +1.0
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+2.5],[+0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-2.5],[-0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[+0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[-0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[+0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[-0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+NaN],[+0.0e+0],[+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+NaN],[-0.0e+0],[+1.000000e+000 (ok)])
+#
+# Other than the preceding special cases, if either x or y is NaN,
+# return "NaN"; (POSIX specifies NO errno assignment).
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+NaN],[+2.5e+0],[+nan (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-NaN],[+2.5e+0],[+nan (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+2.5],[-NaN],   [+nan (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-2.5],[+NaN],   [+nan (ok)])
+#
+# For any odd integer value of y > 0, if |x| == 0, return signed
+# zero, with same sign as x, (i.e. value of x itself).
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+0.0],[+3.0e+0],[+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[+3.0e+0],[-0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+0.0],[+7.0e+0],[+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[+7.0e+0],[-0.000000e+000 (ok)])
+#
+# For any other y > 0, (i.e. +ve but not an odd integer), if |x| == 0,
+# return +0.0
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[+2.5e+0],[+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.0],[+3.5e+4],[+0.000000e+000 (ok)])
+#
+# If x == -1, and y is +ve or -ve infinity, return +1.0
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-1.0],[+Inf],   [+1.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-1.0],[-Inf],   [+1.000000e+000 (ok)])
+#
+# When |x| < 1, and y is infinite, return +ve infinity when y is -ve
+# infinity, or +0.0 when y is +ve infinity.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-0.3],[-Inf],   [+inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+0.7],[-Inf],   [+inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+0.3],[+Inf],   [+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-0.7],[+Inf],   [+0.000000e+000 (ok)])
+#
+# When |x| > 1, and y is infinite, return +0.0 when y is -ve infinity,
+# or +ve infinity when y is +ve infinity.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+5.3],[-Inf],   [+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-1.7],[-Inf],   [+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-5.3],[+Inf],   [+inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+1.7],[+Inf],   [+inf (ok)])
+#
+# If x is -ve infinity, and y < 0, return -ve zero if y is an odd
+# valued -ve integer, otherwise return +ve zero.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[-3.0e+0],[-0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[-2.0e+0],[+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[-3.5e+0],[+0.000000e+000 (ok)])
+#
+# If x is -ve infinity, and y > 0, return -ve infinity if y is an odd
+# valued +ve integer, otherwise return +ve infinity.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[+3.0e+0],[-inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[+2.0e+0],[+inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[-Inf],[+3.5e+0],[+inf (ok)])
+#
+# If x is +ve infinity, and y < 0, return +ve zero.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[-1.0e-4],[+0.000000e+000 (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[-2.5e+4],[+0.000000e+000 (ok)])
+#
+# If x is +ve infinity, and y > 0, return +ve infinity.
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[+1.0e-4],[+inf (ok)])
+MINGW_AT_CHECK_RUN_POW([$1],[+Inf],[+2.5e+4],[+inf (ok)])
+#
+# For result of magnitude greater than largest representable value,
+# (i.e. overflow), report "range error", and return infinity; (this
+# occurs at progressively lesser result boundaries, as we choose to
+# evaluate with decreasing precision, i.e. using powl(), pow(), or
+# powf(), respectively.  Only the first in this progression should
+# pass without overflow in all three cases; the last will overflow
+# in all cases, but to keep it in order, we must define it after
+# each of those which behave differently in each case).
+#
+MINGW_AT_CHECK_RUN_POW([$1],[+2.0],[+2.5e+1],[+3.355443e+007 (ok)])
+])
+
+# Set up the test sequence for the pow() function.
+#
+m4_define([pow_datatype],[double])
+m4_define([pow_strtod],[[strtod("$1",NULL)]])
+m4_define([pow_format],[[%+.6e (%s)\n]])
+#
+MINGW_AT_CHECK_POW_FUNCTION([pow])
+#
+# Add the three variants on the final progression, the first two of
+# which differ in each specific pow(), powf(), and powl() case, and
+# the final one, which is consistent, but we wish to keep it last.
+#
+MINGW_AT_CHECK_RUN_POW([pow],[+2.0],[+2.5e+2],[+1.809251e+075 (ok)])
+MINGW_AT_CHECK_RUN_POW([pow],[+2.0],[+2.5e+3],[+inf (range error)])
+MINGW_AT_CHECK_RUN_POW([pow],[+2.0],[+2.5e+4],[+inf (range error)])
+
+# Repeat for the powf() function.
+#
+m4_define([pow_datatype],[float])
+m4_define([pow_strtod],[[strtof("$1",NULL)]])
+m4_define([pow_format],[[%+.6e (%s)\n]])
+#
+MINGW_AT_CHECK_POW_FUNCTION([powf])
+#
+# Once again, complete the progression of the final test case.
+#
+MINGW_AT_CHECK_RUN_POW([powf],[+2.0],[+2.5e+2],[+inf (range error)])
+MINGW_AT_CHECK_RUN_POW([powf],[+2.0],[+2.5e+3],[+inf (range error)])
+MINGW_AT_CHECK_RUN_POW([powf],[+2.0],[+2.5e+4],[+inf (range error)])
+
+# And finally, for the powl() function.
+#
+m4_define([pow_datatype],[long double])
+m4_define([pow_strtod],[[strtold("$1",NULL)]])
+m4_define([pow_format],[[%+.6Le (%s)\n]])
+#
+MINGW_AT_CHECK_POW_FUNCTION([powl])
+#
+# Once again, complete the progression of the final test case.
+#
+MINGW_AT_CHECK_RUN_POW([powl],[+2.0],[+2.5e+2],[+1.809251e+075 (ok)])
+MINGW_AT_CHECK_RUN_POW([powl],[+2.0],[+2.5e+3],[+3.758280e+752 (ok)])
+MINGW_AT_CHECK_RUN_POW([powl],[+2.0],[+2.5e+4],[+inf (range error)])
+
+# vim: filetype=config formatoptions=croql
+# $RCSfile$: end of file
index 3252925..bfe660c 100644 (file)
@@ -89,6 +89,7 @@ MINGW_AT_LANG([C])
 m4_include([headers.at])
 m4_include([ansiprintf.at])
 m4_include([logarithms.at])
+m4_include([powerfunc.at])
 
 # vim: filetype=config formatoptions=croql
 # $RCSfile$: end of file