# HG changeset patch # User Ben Pfaff # Date 1192910906 25200 # Node ID 96fea5b2eb1126814d5b967da5a0c9a8e2e74502 # Parent f3d25691c7ac1165de8d4ebb232cfca36301bfd6 Implement 'round', 'roundf', 'roundl' modules. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2007-10-20 Ben Pfaff + + * lib/math.in.h: Declare round, roundf, roundl if we are providing + implementations. + * m4/math_h.m4: New substitutions for round, roundf, roundl modules. + * lib/round.c: New file. + * lib/roundf.c: New file. + * lib/roundl.c: New file. + * m4/round.m4: New file. + * m4/roundf.m4: New file. + * m4/roundl.m4: New file. + * m4/check-libm-func-m4: New file. + * modules/math: Replace round, roundf, roundl related @VARS@ in + math.in.h. + * modules/round: New file. + * modules/round-tests: New file. + * modules/roundf: New file. + * modules/roundf-tests: New file. + * modules/roundl: New file. + * modules/roundl-tests: New file. + * tests/test-round1.c: New file. + * tests/test-round2.c: New file. + * tests/test-roundf1.c: New file. + * tests/test-roundf2.c: New file. + * tests/test-roundl.c: New file. + * doc/functions/round.texi: Mention round module. + * doc/functions/roundf.texi: Mention roundf module. + * doc/functions/roundl.texi: Mention roundl module. + * MODULES.html.sh: Mention new modules. + 2007-10-20 Jim Meyering * lib/xprintf.c: Include unconditionally. diff --git a/MODULES.html.sh b/MODULES.html.sh --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1977,6 +1977,9 @@ func_module ldexpl func_module math func_module mathl + func_module round + func_module roundf + func_module roundl func_module signbit func_module trunc func_module truncf diff --git a/doc/functions/round.texi b/doc/functions/round.texi --- a/doc/functions/round.texi +++ b/doc/functions/round.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/round.html} -Gnulib module: --- +Gnulib module: round Portability problems fixed by Gnulib: @itemize -@end itemize - -Portability problems not fixed by Gnulib: -@itemize @item This function is missing on some platforms: FreeBSD 5.2.1, OpenBSD 3.8, AIX 5.1, IRIX 6.5, OSF/1 4.0, Solaris 9, Interix 3.5. @end itemize + +Portability problems not fixed by Gnulib: +@itemize +@end itemize diff --git a/doc/functions/roundf.texi b/doc/functions/roundf.texi --- a/doc/functions/roundf.texi +++ b/doc/functions/roundf.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/roundf.html} -Gnulib module: --- +Gnulib module: roundf Portability problems fixed by Gnulib: @itemize -@end itemize - -Portability problems not fixed by Gnulib: -@itemize @item This function is missing on some platforms: FreeBSD 5.2.1, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 4.0, Solaris 9, Interix 3.5. @end itemize + +Portability problems not fixed by Gnulib: +@itemize +@end itemize diff --git a/doc/functions/roundl.texi b/doc/functions/roundl.texi --- a/doc/functions/roundl.texi +++ b/doc/functions/roundl.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/roundl.html} -Gnulib module: --- +Gnulib module: roundl Portability problems fixed by Gnulib: @itemize -@end itemize - -Portability problems not fixed by Gnulib: -@itemize @item This function is missing on some platforms: FreeBSD 5.2.1, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 4.0, Solaris 9, Cygwin, Interix 3.5, BeOS. @end itemize + +Portability problems not fixed by Gnulib: +@itemize +@end itemize diff --git a/lib/math.in.h b/lib/math.in.h --- a/lib/math.in.h +++ b/lib/math.in.h @@ -217,6 +217,48 @@ #endif +#if @GNULIB_ROUNDF@ +# if !@HAVE_DECL_ROUNDF@ +# define roundf rpl_roundf +extern float roundf (float x); +# endif +#elif defined GNULIB_POSIXCHECK +# undef roundf +# define roundf(x) \ + (GL_LINK_WARNING ("roundf is unportable - " \ + "use gnulib module roundf for portability"), \ + roundf (x)) +#endif + + +#if @GNULIB_ROUND@ +# if !@HAVE_DECL_ROUND@ +# define round rpl_round +extern double round (double x); +# endif +#elif defined GNULIB_POSIXCHECK +# undef round +# define round(x) \ + (GL_LINK_WARNING ("round is unportable - " \ + "use gnulib module round for portability"), \ + round (x)) +#endif + + +#if @GNULIB_ROUNDL@ +# if !@HAVE_DECL_ROUNDL@ +# define roundl rpl_roundl +extern long double roundl (long double x); +# endif +#elif defined GNULIB_POSIXCHECK +# undef roundl +# define roundl(x) \ + (GL_LINK_WARNING ("roundl is unportable - " \ + "use gnulib module roundl for portability"), \ + roundl (x)) +#endif + + #if @GNULIB_MATHL@ || !@HAVE_DECL_SINL@ extern long double sinl (long double x); #endif diff --git a/lib/round.c b/lib/round.c new file mode 100644 --- /dev/null +++ b/lib/round.c @@ -0,0 +1,154 @@ +/* Round toward nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ben Pfaff , 2007. + Based heavily on code by Bruno Haible. */ + +#include + +#include +#include + +#ifdef USE_LONG_DOUBLE +# define ROUND roundl +# define FLOOR floorl +# define CEIL ceill +# define DOUBLE long double +# define MANT_DIG LDBL_MANT_DIG +# define L_(literal) literal##L +# define HAVE_FLOOR_AND_CEIL (HAVE_DECL_FLOORL && HAVE_DECL_CEILL) +#elif ! defined USE_FLOAT +# define ROUND round +# define FLOOR floor +# define CEIL ceil +# define DOUBLE double +# define MANT_DIG DBL_MANT_DIG +# define L_(literal) literal +# define HAVE_FLOOR_AND_CEIL 1 +#else /* defined USE_FLOAT */ +# define ROUND roundf +# define FLOOR floorf +# define CEIL ceilf +# define DOUBLE float +# define MANT_DIG FLT_MANT_DIG +# define L_(literal) literal##f +# define HAVE_FLOOR_AND_CEIL (HAVE_DECL_FLOORF && HAVE_DECL_CEILF) +#endif + +/* If we're being included from test-round2[f].c, it already defined names for + our round implementations. Otherwise, pick the preferred implementation for + this machine. */ +#if !defined FLOOR_BASED_ROUND && !defined FLOOR_FREE_ROUND +# if HAVE_FLOOR_AND_CEIL +# define FLOOR_BASED_ROUND ROUND +# else +# define FLOOR_FREE_ROUND ROUND +# endif +#endif + +#ifdef FLOOR_BASED_ROUND +/* An implementation of the C99 round function based on floor and ceil. We use + this when floor and ceil are available, on the assumption that they are + faster than the open-coded versions below. */ +DOUBLE +FLOOR_BASED_ROUND (DOUBLE x) +{ + if (x >= L_(0.0)) + { + DOUBLE y = FLOOR (x); + if (x - y >= L_(0.5)) + y += L_(1.0); + return y; + } + else + { + DOUBLE y = CEIL (x); + if (y - x >= L_(0.5)) + y -= L_(1.0); + return y; + } +} +#endif /* FLOOR_BASED_ROUND */ + +#ifdef FLOOR_FREE_ROUND +/* An implementation of the C99 round function without floor or ceil. + We use this when floor or ceil is missing. */ +DOUBLE +FLOOR_FREE_ROUND (DOUBLE x) +{ + /* 2^(MANT_DIG-1). */ + static const DOUBLE TWO_MANT_DIG = + /* Assume MANT_DIG <= 5 * 31. + Use the identity + n = floor(n/5) + floor((n+1)/5) + ... + floor((n+4)/5). */ + (DOUBLE) (1U << ((MANT_DIG - 1) / 5)) + * (DOUBLE) (1U << ((MANT_DIG - 1 + 1) / 5)) + * (DOUBLE) (1U << ((MANT_DIG - 1 + 2) / 5)) + * (DOUBLE) (1U << ((MANT_DIG - 1 + 3) / 5)) + * (DOUBLE) (1U << ((MANT_DIG - 1 + 4) / 5)); + + /* The use of 'volatile' guarantees that excess precision bits are dropped at + each addition step and before the following comparison at the caller's + site. It is necessary on x86 systems where double-floats are not IEEE + compliant by default, to avoid that the results become platform and + compiler option dependent. 'volatile' is a portable alternative to gcc's + -ffloat-store option. */ + volatile DOUBLE y = x; + volatile DOUBLE z = y; + + if (z > L_(0.0)) + { + /* Avoid rounding error for x = 0.5 - 2^(-MANT_DIG-1). */ + if (z < L_(0.5)) + z = L_(0.0); + /* Avoid rounding errors for values near 2^k, where k >= MANT_DIG-1. */ + else if (z < TWO_MANT_DIG) + { + /* Add 0.5 to the absolute value. */ + y = z += L_(0.5); + /* Round to the next integer (nearest or up or down, doesn't + matter). */ + z += TWO_MANT_DIG; + z -= TWO_MANT_DIG; + /* Enforce rounding down. */ + if (z > y) + z -= L_(1.0); + } + } + else if (z < L_(0.0)) + { + /* Avoid rounding error for x = -(0.5 - 2^(-MANT_DIG-1)). */ + if (z > - L_(0.5)) + z = L_(0.0); + /* Avoid rounding errors for values near -2^k, where k >= MANT_DIG-1. */ + else if (z > -TWO_MANT_DIG) + { + /* Add 0.5 to the absolute value. */ + y = z -= L_(0.5); + /* Round to the next integer (nearest or up or down, doesn't + matter). */ + z -= TWO_MANT_DIG; + z += TWO_MANT_DIG; + /* Enforce rounding up. */ + if (z < y) + z += L_(1.0); + } + } + return z; +} +#endif /* FLOOR_FREE_ROUND */ + diff --git a/lib/roundf.c b/lib/roundf.c new file mode 100644 --- /dev/null +++ b/lib/roundf.c @@ -0,0 +1,19 @@ +/* Round toward nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#define USE_FLOAT +#include "round.c" diff --git a/lib/roundl.c b/lib/roundl.c new file mode 100644 --- /dev/null +++ b/lib/roundl.c @@ -0,0 +1,19 @@ +/* Round toward nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#define USE_LONG_DOUBLE +#include "round.c" diff --git a/m4/check-libm-func.m4 b/m4/check-libm-func.m4 new file mode 100644 --- /dev/null +++ b/m4/check-libm-func.m4 @@ -0,0 +1,51 @@ +# check-libm.m4 serial 1 +dnl Copyright (C) 2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl +dnl AC_CHECK_LIBM_FUNC (MATH_FUNCTION, INVOCATION, +dnl [RUN-IF-FOUND], [RUN-IF-NOT-FOUND]) +dnl +dnl Checks for a declaration of the given MATH_FUNCTION in , and +dnl substitutes HAVE_DECL_ accordingly. If a declaration is found, +dnl determines the needed library (if any), assigns it to _LIBM, and +dnl executes RUN-IF-FOUND; otherwise, executes RUN-IF-NOT-FOUND. +dnl +dnl INVOCATION should be a C statement that invokes MATH_FUNCTION, both +dnl using and assigning back to double variable 'x', e.g. "x = floor +dnl (x);". +AC_DEFUN([gl_CHECK_LIBM_FUNC], +[ +m4_pushdef([FUNC_LIBM], m4_toupper([$1])[_LIBM])dnl +m4_pushdef([HAVE_DECL_FUNC], HAVE_DECL_[]m4_toupper([$1]))dnl + AC_CHECK_DECLS([$1], , , [#include ]) + if test "$ac_cv_have_decl_$1" = yes; then + save_LIBS=$LIBS + FUNC_LIBM=? + for libm in "" "-lm"; do + LIBS="$save_LIBS $libm" + AC_TRY_LINK([ + #ifndef __NO_MATH_INLINES + # define __NO_MATH_INLINES 1 /* for glibc */ + #endif + #include + double x;], + [$2], + [FUNC_LIBM=$libm +break]) + done + LIBS=$save_LIBS + if test "$FUNC_LIBM" = "?"; then + FUNC_LIBM= + fi +m4_ifvaln([$3], [$3])dnl + else + HAVE_DECL_FUNC= + FUNC_LIBM= +m4_ifvaln([$4], [$4])dnl + fi + AC_SUBST(HAVE_DECL_FUNC) + AC_SUBST(FUNC_LIBM) +m4_popdef([FUNC_LIBM]) +m4_popdef([HAVE_DECL_FUNC])]) diff --git a/m4/math_h.m4 b/m4/math_h.m4 --- a/m4/math_h.m4 +++ b/m4/math_h.m4 @@ -27,6 +27,9 @@ GNULIB_FREXPL=0; AC_SUBST([GNULIB_FREXPL]) GNULIB_LDEXPL=0; AC_SUBST([GNULIB_LDEXPL]) GNULIB_MATHL=0; AC_SUBST([GNULIB_MATHL]) + GNULIB_ROUND=0; AC_SUBST([GNULIB_ROUND]) + GNULIB_ROUNDF=0; AC_SUBST([GNULIB_ROUNDF]) + GNULIB_ROUNDL=0; AC_SUBST([GNULIB_ROUNDL]) GNULIB_SIGNBIT=0; AC_SUBST([GNULIB_SIGNBIT]) GNULIB_TRUNC=0; AC_SUBST([GNULIB_TRUNC]) GNULIB_TRUNCF=0; AC_SUBST([GNULIB_TRUNCF]) @@ -44,6 +47,9 @@ HAVE_DECL_FREXPL=1; AC_SUBST([HAVE_DECL_FREXPL]) HAVE_DECL_LDEXPL=1; AC_SUBST([HAVE_DECL_LDEXPL]) HAVE_DECL_LOGL=1; AC_SUBST([HAVE_DECL_LOGL]) + HAVE_DECL_ROUND=1; AC_SUBST([HAVE_DECL_ROUND]) + HAVE_DECL_ROUNDF=1; AC_SUBST([HAVE_DECL_ROUNDF]) + HAVE_DECL_ROUNDL=1; AC_SUBST([HAVE_DECL_ROUNDL]) HAVE_DECL_SINL=1; AC_SUBST([HAVE_DECL_SINL]) HAVE_DECL_SQRTL=1; AC_SUBST([HAVE_DECL_SQRTL]) HAVE_DECL_TANL=1; AC_SUBST([HAVE_DECL_TANL]) diff --git a/m4/round.m4 b/m4/round.m4 new file mode 100644 --- /dev/null +++ b/m4/round.m4 @@ -0,0 +1,15 @@ +# round.m4 serial 1 +dnl Copyright (C) 2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_ROUND], +[ + AC_REQUIRE([gl_MATH_H_DEFAULTS]) + dnl Persuade glibc to declare round(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + gl_CHECK_LIBM_FUNC([round], [x = round(x);], [], [ + AC_REQUIRE([gl_FUNC_FLOOR]) + ROUND_LIBM=$FLOOR_LIBM + AC_LIBOBJ([round])])]) diff --git a/m4/roundf.m4 b/m4/roundf.m4 new file mode 100644 --- /dev/null +++ b/m4/roundf.m4 @@ -0,0 +1,22 @@ +# roundf.m4 serial 1 +dnl Copyright (C) 2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_ROUNDF], +[ + AC_REQUIRE([gl_MATH_H_DEFAULTS]) + dnl Persuade glibc to declare roundf(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + dnl Test whether roundf() is declared. + gl_CHECK_LIBM_FUNC([roundf], [x = roundf(x);], [], [ + dnl No. Are both floorf() and ceilf() available? If so then we can use + dnl them to implement roundf(), on the assumption that they're fast. + gl_CHECK_LIBM_FUNC([floorf], [x = floorf(x);], [ + AC_CHECK_DECL([ceilf], + [dnl Yes. Both are declared. Link against the necessary library. + ROUNDF_LIBM="$FLOORF_LIBM"], + [: dnl No. We will use an implementation that doesn't need them. +], [#include +])])])]) diff --git a/m4/roundl.m4 b/m4/roundl.m4 new file mode 100644 --- /dev/null +++ b/m4/roundl.m4 @@ -0,0 +1,22 @@ +# roundl.m4 serial 1 +dnl Copyright (C) 2007 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_ROUNDL], +[ + AC_REQUIRE([gl_MATH_H_DEFAULTS]) + dnl Persuade glibc to declare roundl(). + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + dnl Test whether roundl() is declared. + gl_CHECK_LIBM_FUNC([roundl], [x = roundl(x);], [], [ + dnl No. Are both floorl() and ceill() available? If so then we can use + dnl them to implement roundl(), on the assumption that they're fast. + gl_CHECK_LIBM_FUNC([floorl], [x = floorl(x);], [ + AC_CHECK_DECL([ceill], + [dnl Yes. Both are declared. Link against the necessary library. + ROUNDL_LIBM="$FLOORL_LIBM"], + [: dnl No. We will use an implementation that doesn't need them. +], [#include +])])])]) diff --git a/modules/math b/modules/math --- a/modules/math +++ b/modules/math @@ -30,6 +30,9 @@ -e 's|@''GNULIB_FREXPL''@|$(GNULIB_FREXPL)|g' \ -e 's|@''GNULIB_LDEXPL''@|$(GNULIB_LDEXPL)|g' \ -e 's|@''GNULIB_MATHL''@|$(GNULIB_MATHL)|g' \ + -e 's|@''GNULIB_ROUND''@|$(GNULIB_ROUND)|g' \ + -e 's|@''GNULIB_ROUNDF''@|$(GNULIB_ROUNDF)|g' \ + -e 's|@''GNULIB_ROUNDL''@|$(GNULIB_ROUNDL)|g' \ -e 's|@''GNULIB_SIGNBIT''@|$(GNULIB_SIGNBIT)|g' \ -e 's|@''GNULIB_TRUNC''@|$(GNULIB_TRUNC)|g' \ -e 's|@''GNULIB_TRUNCF''@|$(GNULIB_TRUNCF)|g' \ @@ -46,6 +49,9 @@ -e 's|@''HAVE_DECL_FREXPL''@|$(HAVE_DECL_FREXPL)|g' \ -e 's|@''HAVE_DECL_LDEXPL''@|$(HAVE_DECL_LDEXPL)|g' \ -e 's|@''HAVE_DECL_LOGL''@|$(HAVE_DECL_LOGL)|g' \ + -e 's|@''HAVE_DECL_ROUND''@|$(HAVE_DECL_ROUND)|g' \ + -e 's|@''HAVE_DECL_ROUNDF''@|$(HAVE_DECL_ROUNDF)|g' \ + -e 's|@''HAVE_DECL_ROUNDL''@|$(HAVE_DECL_ROUNDL)|g' \ -e 's|@''HAVE_DECL_SINL''@|$(HAVE_DECL_SINL)|g' \ -e 's|@''HAVE_DECL_SQRTL''@|$(HAVE_DECL_SQRTL)|g' \ -e 's|@''HAVE_DECL_TANL''@|$(HAVE_DECL_TANL)|g' \ diff --git a/modules/round b/modules/round new file mode 100644 --- /dev/null +++ b/modules/round @@ -0,0 +1,32 @@ +Description: +round() function: round toward nearest, breaking ties away from zero. + +Files: +lib/round.c +m4/check-libm-func.m4 +m4/round.m4 + +Depends-on: +float +floor +math +extensions + +configure.ac: +gl_FUNC_ROUND +gl_MATH_MODULE_INDICATOR([round]) + +Makefile.am: + +Include: + + +Link: +$(ROUND_LIBM) + +License: +LGPL + +Maintainer: +Ben Pfaff + diff --git a/modules/round-tests b/modules/round-tests new file mode 100644 --- /dev/null +++ b/modules/round-tests @@ -0,0 +1,21 @@ +Files: +tests/test-round1.c +tests/test-round2.c + +Depends-on: +isnan-nolibm +stdbool +stdint +fprintf-posix +verify + +configure.ac: + +Makefile.am: +TESTS += test-round1 test-round2 +check_PROGRAMS += test-round1 test-round2 +test_round1_LDADD = $(LDADD) @ROUND_LIBM@ +test_round2_LDADD = $(LDADD) @ROUND_LIBM@ + +License: +LGPL diff --git a/modules/roundf b/modules/roundf new file mode 100644 --- /dev/null +++ b/modules/roundf @@ -0,0 +1,32 @@ +Description: +roundf() function: round toward nearest, breaking ties away from zero. + +Files: +lib/round.c +lib/roundf.c +m4/check-libm-func.m4 +m4/roundf.m4 + +Depends-on: +float +math +extensions + +configure.ac: +gl_FUNC_ROUNDF +gl_MATH_MODULE_INDICATOR([roundf]) + +Makefile.am: + +Include: + + +Link: +$(ROUNDF_LIBM) + +License: +LGPL + +Maintainer: +Ben Pfaff + diff --git a/modules/roundf-tests b/modules/roundf-tests new file mode 100644 --- /dev/null +++ b/modules/roundf-tests @@ -0,0 +1,22 @@ +Files: +tests/test-roundf1.c +tests/test-round2.c +tests/test-roundf2.c + +Depends-on: +isnanf-nolibm +stdbool +stdint +fprintf-posix +verify + +configure.ac: + +Makefile.am: +TESTS += test-roundf1 test-roundf2 +check_PROGRAMS += test-roundf1 test-roundf2 +test_roundf1_LDADD = $(LDADD) @ROUNDF_LIBM@ +test_roundf2_LDADD = $(LDADD) @ROUNDF_LIBM@ + +License: +LGPL diff --git a/modules/roundl b/modules/roundl new file mode 100644 --- /dev/null +++ b/modules/roundl @@ -0,0 +1,32 @@ +Description: +roundl() function: round toward nearest, breaking ties away from zero. + +Files: +lib/round.c +lib/roundl.c +m4/check-libm-func.m4 +m4/roundl.m4 + +Depends-on: +float +math +extensions + +configure.ac: +gl_FUNC_ROUNDL +gl_MATH_MODULE_INDICATOR([roundl]) + +Makefile.am: + +Include: + + +Link: +$(ROUNDL_LIBM) + +License: +LGPL + +Maintainer: +Ben Pfaff + diff --git a/modules/roundl-tests b/modules/roundl-tests new file mode 100644 --- /dev/null +++ b/modules/roundl-tests @@ -0,0 +1,16 @@ +Files: +tests/test-roundl.c + +Depends-on: +fpucw +isnanl-nolibm + +configure.ac: + +Makefile.am: +TESTS += test-roundl +check_PROGRAMS += test-roundl +test_roundl_LDADD = $(LDADD) @ROUNDL_LIBM@ + +License: +LGPL diff --git a/tests/test-round1.c b/tests/test-round1.c new file mode 100644 --- /dev/null +++ b/tests/test-round1.c @@ -0,0 +1,90 @@ +/* Test of rounding to nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ben Pfaff , 2007. + Based heavily on Bruno Haible's test-trunc.c. */ + +#include + +#include + +#include +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + abort (); \ + } \ + } \ + while (0) + +/* The Compaq (ex-DEC) C 6.4 compiler chokes on the expression 0.0 / 0.0. */ +#ifdef __DECC +static double +NaN () +{ + static double zero = 0.0; + return zero / zero; +} +#else +# define NaN() (0.0 / 0.0) +#endif + +int +main () +{ + /* Zero. */ + ASSERT (round (0.0) == 0.0); + ASSERT (round (-0.0) == 0.0); + /* Positive numbers. */ + ASSERT (round (0.3) == 0.0); + ASSERT (round (0.5) == 1.0); + ASSERT (round (0.7) == 1.0); + ASSERT (round (1.0) == 1.0); + ASSERT (round (1.5) == 2.0); + ASSERT (round (2.5) == 3.0); + ASSERT (round (1.999) == 2.0); + ASSERT (round (2.0) == 2.0); + ASSERT (round (65535.999) == 65536.0); + ASSERT (round (65536.0) == 65536.0); + ASSERT (round (65536.001) == 65536.0); + ASSERT (round (2.341e31) == 2.341e31); + /* Negative numbers. */ + ASSERT (round (-0.3) == 0.0); + ASSERT (round (-0.5) == -1.0); + ASSERT (round (-0.7) == -1.0); + ASSERT (round (-1.0) == -1.0); + ASSERT (round (-1.5) == -2.0); + ASSERT (round (-2.5) == -3.0); + ASSERT (round (-1.999) == -2.0); + ASSERT (round (-2.0) == -2.0); + ASSERT (round (-65535.999) == -65536.0); + ASSERT (round (-65536.0) == -65536.0); + ASSERT (round (-65536.001) == -65536.0); + ASSERT (round (-2.341e31) == -2.341e31); + /* Infinite numbers. */ + ASSERT (round (1.0 / 0.0) == 1.0 / 0.0); + ASSERT (round (-1.0 / 0.0) == -1.0 / 0.0); + /* NaNs. */ + ASSERT (isnan (round (NaN ()))); + + return 0; +} diff --git a/tests/test-round2.c b/tests/test-round2.c new file mode 100644 --- /dev/null +++ b/tests/test-round2.c @@ -0,0 +1,108 @@ +/* Test of rounding to nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Ben Pfaff , 2007. + Heavily based on code by Bruno Haible. */ + +/* Get the two reference implementations of round under the names + round_reference1 and round_reference2. + + round.c will #include for us. */ +#define FLOOR_BASED_ROUND round_reference1 +#define FLOOR_FREE_ROUND round_reference2 +#include "round.c" + +#include +#include +#include +#include +#include +#include + +#include "verify.h" + +#ifdef USE_LONG_DOUBLE +# error Long double not supported. +#elif ! defined USE_FLOAT +# include "isnan.h" +# define ISNAN isnan +# define FUNCTION "round" +# define DOUBLE_UINT uint64_t +# define DOUBLE_BITS 64 +# define NUM_HIGHBITS 13 +# define NUM_LOWBITS 4 +#else /* defined USE_FLOAT */ +# include "isnanf.h" +# define ISNAN isnanf +# define FUNCTION "roundf" +# define DOUBLE_UINT uint32_t +# define DOUBLE_BITS 32 +# define NUM_HIGHBITS 12 +# define NUM_LOWBITS 4 +#endif + +/* Test for equality. */ +static bool +equal (const char *message, DOUBLE x, DOUBLE y0, DOUBLE y1) +{ + if (ISNAN (y0) ? ISNAN (y1) : y0 == y1) + return true; + else + { + fprintf (stderr, "%s: "FUNCTION"(%g(%a)) = %g(%a) or %g(%a)?\n", + message, x, x, y0, y0, y1, y1); + return false; + } +} + +/* Test the function for a given argument. */ +static bool +check (DOUBLE x) +{ + DOUBLE ref1 = round_reference1 (x); + DOUBLE ref2 = round_reference2 (x); + DOUBLE result = round (x); + + /* If the reference implementations disagree, bail out immediately. */ + if (!equal ("reference implementations disagree", x, ref1, ref2)) + exit (EXIT_FAILURE); + + /* If the actual implementation is wrong, return an error code. */ + return equal ("bad round implementation", x, ref1, result); +} + +int +main (void) +{ + DOUBLE_UINT highbits, lowbits; + int error = 0; + for (highbits = 0; highbits < (1 << NUM_HIGHBITS); highbits++) + for (lowbits = 0; lowbits < (1 << NUM_LOWBITS); lowbits++) + { + /* Combine highbits and lowbits into a floating-point number, + sign-extending the lowbits to DOUBLE_BITS-NUM_HIGHBITS bits. */ + union { DOUBLE f; DOUBLE_UINT i; } janus; + verify (sizeof janus.f == sizeof janus.i); + janus.i = lowbits | (highbits << (DOUBLE_BITS - NUM_HIGHBITS)); + if (lowbits >> (NUM_LOWBITS - 1)) + janus.i |= ((DOUBLE_UINT) -1 + >> (NUM_LOWBITS + NUM_HIGHBITS) + << NUM_LOWBITS); + if (!check (janus.f)) + error = true; + } + return (error ? 1 : 0); +} diff --git a/tests/test-roundf1.c b/tests/test-roundf1.c new file mode 100644 --- /dev/null +++ b/tests/test-roundf1.c @@ -0,0 +1,90 @@ +/* Test of rounding to nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ben Pfaff , 2007. + Based heavily on Bruno Haible's test-truncf.c. */ + +#include + +#include + +#include +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + abort (); \ + } \ + } \ + while (0) + +/* The Compaq (ex-DEC) C 6.4 compiler chokes on the expression 0.0 / 0.0. */ +#ifdef __DECC +static float +NaN () +{ + static float zero = 0.0f; + return zero / zero; +} +#else +# define NaN() (0.0f / 0.0f) +#endif + +int +main () +{ + /* Zero. */ + ASSERT (roundf (0.0f) == 0.0f); + ASSERT (roundf (-0.0f) == 0.0f); + /* Positive numbers. */ + ASSERT (roundf (0.3f) == 0.0f); + ASSERT (roundf (0.5f) == 1.0f); + ASSERT (roundf (0.7f) == 1.0f); + ASSERT (roundf (1.0f) == 1.0f); + ASSERT (roundf (1.5f) == 2.0f); + ASSERT (roundf (2.5f) == 3.0f); + ASSERT (roundf (1.999f) == 2.0f); + ASSERT (roundf (2.0f) == 2.0f); + ASSERT (roundf (65535.99f) == 65536.0f); + ASSERT (roundf (65536.0f) == 65536.0f); + ASSERT (roundf (65536.01f) == 65536.0f); + ASSERT (roundf (2.341e31f) == 2.341e31f); + /* Negative numbers. */ + ASSERT (roundf (-0.3f) == 0.0f); + ASSERT (roundf (-0.5f) == -1.0f); + ASSERT (roundf (-0.7f) == -1.0f); + ASSERT (roundf (-1.0f) == -1.0f); + ASSERT (roundf (-1.5f) == -2.0f); + ASSERT (roundf (-2.5f) == -3.0f); + ASSERT (roundf (-1.999f) == -2.0f); + ASSERT (roundf (-2.0f) == -2.0f); + ASSERT (roundf (-65535.99f) == -65536.0f); + ASSERT (roundf (-65536.0f) == -65536.0f); + ASSERT (roundf (-65536.01f) == -65536.0f); + ASSERT (roundf (-2.341e31f) == -2.341e31f); + /* Infinite numbers. */ + ASSERT (roundf (1.0 / 0.0f) == 1.0 / 0.0f); + ASSERT (roundf (-1.0 / 0.0f) == -1.0 / 0.0f); + /* NaNs. */ + ASSERT (isnan (roundf (NaN ()))); + + return 0; +} diff --git a/tests/test-roundf2.c b/tests/test-roundf2.c new file mode 100644 --- /dev/null +++ b/tests/test-roundf2.c @@ -0,0 +1,2 @@ +#define USE_FLOAT +#include "test-round2.c" diff --git a/tests/test-roundl.c b/tests/test-roundl.c new file mode 100644 --- /dev/null +++ b/tests/test-roundl.c @@ -0,0 +1,85 @@ +/* Test of rounding to nearest, breaking ties away from zero. + Copyright (C) 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ben Pfaff , 2007. + Based heavily on Bruno Haible's test-truncl.c. */ + +#include + +#include + +#include +#include + +#include "fpucw.h" +#include "isnanl-nolibm.h" + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + abort (); \ + } \ + } \ + while (0) + +int +main () +{ + DECL_LONG_DOUBLE_ROUNDING + + BEGIN_LONG_DOUBLE_ROUNDING (); + + /* Zero. */ + ASSERT (roundl (0.0L) == 0.0L); + ASSERT (roundl (-0.0L) == 0.0L); + /* Positive numbers. */ + ASSERT (roundl (0.3L) == 0.0L); + ASSERT (roundl (0.5L) == 1.0L); + ASSERT (roundl (0.7L) == 1.0L); + ASSERT (roundl (1.0L) == 1.0L); + ASSERT (roundl (1.5L) == 2.0L); + ASSERT (roundl (2.5L) == 3.0L); + ASSERT (roundl (1.999L) == 2.0L); + ASSERT (roundl (2.0L) == 2.0L); + ASSERT (roundl (65535.999L) == 65536.0L); + ASSERT (roundl (65536.0L) == 65536.0L); + ASSERT (roundl (65536.001L) == 65536.0L); + ASSERT (roundl (2.341e31L) == 2.341e31L); + /* Negative numbers. */ + ASSERT (roundl (-0.3L) == 0.0L); + ASSERT (roundl (-0.5L) == -1.0L); + ASSERT (roundl (-0.7L) == -1.0L); + ASSERT (roundl (-1.0L) == -1.0L); + ASSERT (roundl (-1.5L) == -2.0L); + ASSERT (roundl (-2.5L) == -3.0L); + ASSERT (roundl (-1.999L) == -2.0L); + ASSERT (roundl (-2.0L) == -2.0L); + ASSERT (roundl (-65535.999L) == -65536.0L); + ASSERT (roundl (-65536.0L) == -65536.0L); + ASSERT (roundl (-65536.001L) == -65536.0L); + ASSERT (roundl (-2.341e31L) == -2.341e31L); + /* Infinite numbers. */ + ASSERT (roundl (1.0 / 0.0L) == 1.0 / 0.0L); + ASSERT (roundl (-1.0 / 0.0L) == -1.0 / 0.0L); + /* NaNs. */ + ASSERT (isnanl (roundl (0.0L / 0.0L))); + + return 0; +}