[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
remainder, remainderf, remainderl: rewrite
From: |
Bruno Haible |
Subject: |
remainder, remainderf, remainderl: rewrite |
Date: |
Sun, 04 Mar 2012 23:02:15 +0100 |
User-agent: |
KMail/4.7.4 (Linux/3.1.0-1.2-desktop; KDE/4.7.4; x86_64; ; ) |
> The additional unit tests for fmod() and fmodl() revealed that gnulib's
> replacement algorithm returned wrong results when the quotient of the
> two arguments was large. For example:
> fmod (1e30, 1.5)
The gnulib implementation of the remainder() function family had the same
bug. Corrected as follows.
2012-03-04 Bruno Haible <address@hidden>
remainder, remainderf, remainderl: Fix computation for large quotients.
* lib/remainder.c: Completely rewritten.
* lib/remainderf.c (remainderf): Use implementation of remainder.c with
USE_FLOAT.
* lib/remainderl.c (remainderl): Use implementation of remainder.c with
USE_LONG_DOUBLE.
* modules/remainder (Depends-on): Add isfinite, signbit, fabs, fmod,
isnand, isinf. Remove round, fma.
* modules/remainderf (Files): Add lib/remainder.c.
(Depends-on): Add isfinite, signbit, fabsf, fmodf, isnanf, isinf.
Remove roundf, fmaf.
* modules/remainderl (Files): Add lib/remainder.c.
(Depends-on): Add float, isfinite, signbit, fabsl, fmodl, isnanl,
isinf. Remove roundl, fmal.
* m4/remainder.m4 (gl_FUNC_REMAINDER): Update computation of
REMAINDER_LIBM.
* m4/remainderf.m4 (gl_FUNC_REMAINDERF): Update computation of
REMAINDERF_LIBM.
* m4/remainderl.m4 (gl_FUNC_REMAINDERL): Update computation of
REMAINDERL_LIBM.
=============================== lib/remainder.c ===============================
/* Remainder.
Copyright (C) 2012 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 <http://www.gnu.org/licenses/>. */
#if ! (defined USE_LONG_DOUBLE || defined USE_FLOAT)
# include <config.h>
#endif
/* Specification. */
#include <math.h>
#ifdef USE_LONG_DOUBLE
# define REMAINDER remainderl
# define DOUBLE long double
# define L_(literal) literal##L
# define FABS fabsl
# define FMOD fmodl
# define ISNAN isnanl
#elif ! defined USE_FLOAT
# define REMAINDER remainder
# define DOUBLE double
# define L_(literal) literal
# define FABS fabs
# define FMOD fmod
# define ISNAN isnand
#else /* defined USE_FLOAT */
# define REMAINDER remainderf
# define DOUBLE float
# define L_(literal) literal##f
# define FABS fabsf
# define FMOD fmodf
# define ISNAN isnanf
#endif
#undef NAN
#if defined _MSC_VER
static DOUBLE zero;
# define NAN (zero / zero)
#else
# define NAN (L_(0.0) / L_(0.0))
#endif
DOUBLE
REMAINDER (DOUBLE x, DOUBLE y)
{
if (isfinite (x) && isfinite (y) && y != L_(0.0))
{
if (x == L_(0.0))
/* Return x, regardless of the sign of y. */
return x;
{
int negate = ((!signbit (x)) ^ (!signbit (y)));
DOUBLE r;
/* Take the absolute value of x and y. */
x = FABS (x);
y = FABS (y);
/* Trivial case that requires no computation. */
if (x <= L_(0.5) * y)
return (negate ? - x : x);
/* With a fixed y, the function x -> remainder(x,y) has a period 2*y.
Therefore we can reduce the argument x modulo 2*y. And it's no
problem if 2*y overflows, since fmod(x,Inf) = x. */
x = FMOD (x, L_(2.0) * y);
/* Consider the 3 cases:
0 <= x <= 0.5 * y
0.5 * y < x < 1.5 * y
1.5 * y <= x <= 2.0 * y */
if (x <= L_(0.5) * y)
r = x;
else
{
r = x - y;
if (r > L_(0.5) * y)
r = x - L_(2.0) * y;
}
return (negate ? - r : r);
}
}
else
{
if (ISNAN (x) || ISNAN (y))
return x + y; /* NaN */
else if (isinf (y))
return x;
else
/* x infinite or y zero */
return NAN;
}
}
===============================================================================
--- lib/remainderf.c.orig Sun Mar 4 22:56:28 2012
+++ lib/remainderf.c Sun Mar 4 22:05:15 2012
@@ -19,32 +19,17 @@
/* Specification. */
#include <math.h>
+#if HAVE_REMAINDER
+
float
remainderf (float x, float y)
{
-#if HAVE_REMAINDER
return (float) remainder ((double) x, (double) y);
+}
+
#else
- float q = - roundf (x / y);
- float r = fmaf (q, y, x); /* = x + q * y, computed in one step */
- /* Correct possible rounding errors in the quotient x / y. */
- float half_y = 0.5L * y;
- if (y >= 0)
- {
- /* Expect -y/2 <= r <= y/2. */
- if (r > half_y)
- q -= 1, r = fmaf (q, y, x);
- else if (r < - half_y)
- q += 1, r = fmaf (q, y, x);
- }
- else
- {
- /* Expect y/2 <= r <= -y/2. */
- if (r > - half_y)
- q += 1, r = fmaf (q, y, x);
- else if (r < half_y)
- q -= 1, r = fmaf (q, y, x);
- }
- return r;
+
+# define USE_FLOAT
+# include "remainder.c"
+
#endif
-}
--- lib/remainderl.c.orig Sun Mar 4 22:56:28 2012
+++ lib/remainderl.c Sun Mar 4 22:05:15 2012
@@ -29,30 +29,7 @@
#else
-long double
-remainderl (long double x, long double y)
-{
- long double q = - roundl (x / y);
- long double r = fmal (q, y, x); /* = x + q * y, computed in one step */
- /* Correct possible rounding errors in the quotient x / y. */
- long double half_y = 0.5L * y;
- if (y >= 0)
- {
- /* Expect -y/2 <= r <= y/2. */
- if (r > half_y)
- q -= 1, r = fmal (q, y, x);
- else if (r < - half_y)
- q += 1, r = fmal (q, y, x);
- }
- else
- {
- /* Expect y/2 <= r <= -y/2. */
- if (r > - half_y)
- q += 1, r = fmal (q, y, x);
- else if (r < half_y)
- q -= 1, r = fmal (q, y, x);
- }
- return r;
-}
+# define USE_LONG_DOUBLE
+# include "remainder.c"
#endif
--- m4/remainder.m4.orig Sun Mar 4 22:56:28 2012
+++ m4/remainder.m4 Sun Mar 4 22:22:05 2012
@@ -1,4 +1,4 @@
-# remainder.m4 serial 2
+# remainder.m4 serial 3
dnl Copyright (C) 2012 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -104,18 +104,24 @@
fi
if test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1; then
dnl Find libraries needed to link lib/remainder.c.
- AC_REQUIRE([gl_FUNC_ROUND])
- AC_REQUIRE([gl_FUNC_FMA])
+ AC_REQUIRE([gl_FUNC_FABS])
+ AC_REQUIRE([gl_FUNC_FMOD])
+ AC_REQUIRE([gl_FUNC_ISNAND])
REMAINDER_LIBM=
- dnl Append $ROUND_LIBM to REMAINDER_LIBM, avoiding gratuitous duplicates.
+ dnl Append $FABS_LIBM to REMAINDER_LIBM, avoiding gratuitous duplicates.
case " $REMAINDER_LIBM " in
- *" $ROUND_LIBM "*) ;;
- *) REMAINDER_LIBM="$REMAINDER_LIBM $ROUND_LIBM" ;;
+ *" $FABS_LIBM "*) ;;
+ *) REMAINDER_LIBM="$REMAINDER_LIBM $FABS_LIBM" ;;
esac
- dnl Append $FMA_LIBM to REMAINDER_LIBM, avoiding gratuitous duplicates.
+ dnl Append $FMOD_LIBM to REMAINDER_LIBM, avoiding gratuitous duplicates.
case " $REMAINDER_LIBM " in
- *" $FMA_LIBM "*) ;;
- *) REMAINDER_LIBM="$REMAINDER_LIBM $FMA_LIBM" ;;
+ *" $FMOD_LIBM "*) ;;
+ *) REMAINDER_LIBM="$REMAINDER_LIBM $FMOD_LIBM" ;;
+ esac
+ dnl Append $ISNAND_LIBM to REMAINDER_LIBM, avoiding gratuitous duplicates.
+ case " $REMAINDER_LIBM " in
+ *" $ISNAND_LIBM "*) ;;
+ *) REMAINDER_LIBM="$REMAINDER_LIBM $ISNAND_LIBM" ;;
esac
fi
AC_SUBST([REMAINDER_LIBM])
--- m4/remainderf.m4.orig Sun Mar 4 22:56:28 2012
+++ m4/remainderf.m4 Sun Mar 4 22:24:23 2012
@@ -1,4 +1,4 @@
-# remainderf.m4 serial 2
+# remainderf.m4 serial 3
dnl Copyright (C) 2012 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -91,18 +91,24 @@
[Define to 1 if the remainder() function is available in libc or
libm.])
REMAINDERF_LIBM="$REMAINDER_LIBM"
else
- AC_REQUIRE([gl_FUNC_ROUNDF])
- AC_REQUIRE([gl_FUNC_FMAF])
+ AC_REQUIRE([gl_FUNC_FABSF])
+ AC_REQUIRE([gl_FUNC_FMODF])
+ AC_REQUIRE([gl_FUNC_ISNANF])
REMAINDERF_LIBM=
- dnl Append $ROUNDF_LIBM to REMAINDERF_LIBM, avoiding gratuitous
duplicates.
+ dnl Append $FABSF_LIBM to REMAINDERF_LIBM, avoiding gratuitous
duplicates.
case " $REMAINDERF_LIBM " in
- *" $ROUNDF_LIBM "*) ;;
- *) REMAINDERF_LIBM="$REMAINDERF_LIBM $ROUNDF_LIBM" ;;
+ *" $FABSF_LIBM "*) ;;
+ *) REMAINDERF_LIBM="$REMAINDERF_LIBM $FABSF_LIBM" ;;
esac
- dnl Append $FMAF_LIBM to REMAINDERF_LIBM, avoiding gratuitous duplicates.
+ dnl Append $FMODF_LIBM to REMAINDERF_LIBM, avoiding gratuitous
duplicates.
case " $REMAINDERF_LIBM " in
- *" $FMAF_LIBM "*) ;;
- *) REMAINDERF_LIBM="$REMAINDERF_LIBM $FMAF_LIBM" ;;
+ *" $FMODF_LIBM "*) ;;
+ *) REMAINDERF_LIBM="$REMAINDERF_LIBM $FMODF_LIBM" ;;
+ esac
+ dnl Append $ISNANF_LIBM to REMAINDERF_LIBM, avoiding gratuitous
duplicates.
+ case " $REMAINDERF_LIBM " in
+ *" $ISNANF_LIBM "*) ;;
+ *) REMAINDERF_LIBM="$REMAINDERF_LIBM $ISNANF_LIBM" ;;
esac
fi
fi
--- m4/remainderl.m4.orig Sun Mar 4 22:56:29 2012
+++ m4/remainderl.m4 Sun Mar 4 22:25:58 2012
@@ -1,4 +1,4 @@
-# remainderl.m4 serial 2
+# remainderl.m4 serial 3
dnl Copyright (C) 2012 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -88,18 +88,24 @@
if test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1; then
REMAINDERL_LIBM="$REMAINDER_LIBM"
else
- AC_REQUIRE([gl_FUNC_ROUNDL])
- AC_REQUIRE([gl_FUNC_FMAL])
+ AC_REQUIRE([gl_FUNC_FABSL])
+ AC_REQUIRE([gl_FUNC_FMODL])
+ AC_REQUIRE([gl_FUNC_ISNANL])
REMAINDERL_LIBM=
- dnl Append $ROUNDL_LIBM to REMAINDERL_LIBM, avoiding gratuitous
duplicates.
+ dnl Append $FABSL_LIBM to REMAINDERL_LIBM, avoiding gratuitous
duplicates.
case " $REMAINDERL_LIBM " in
- *" $ROUNDL_LIBM "*) ;;
- *) REMAINDERL_LIBM="$REMAINDERL_LIBM $ROUNDL_LIBM" ;;
+ *" $FABSL_LIBM "*) ;;
+ *) REMAINDERL_LIBM="$REMAINDERL_LIBM $FABSL_LIBM" ;;
esac
- dnl Append $FMAL_LIBM to REMAINDERL_LIBM, avoiding gratuitous duplicates.
+ dnl Append $FMODL_LIBM to REMAINDERL_LIBM, avoiding gratuitous
duplicates.
case " $REMAINDERL_LIBM " in
- *" $FMAL_LIBM "*) ;;
- *) REMAINDERL_LIBM="$REMAINDERL_LIBM $FMAL_LIBM" ;;
+ *" $FMODL_LIBM "*) ;;
+ *) REMAINDERL_LIBM="$REMAINDERL_LIBM $FMODL_LIBM" ;;
+ esac
+ dnl Append $ISNANL_LIBM to REMAINDERL_LIBM, avoiding gratuitous
duplicates.
+ case " $REMAINDERL_LIBM " in
+ *" $ISNANL_LIBM "*) ;;
+ *) REMAINDERL_LIBM="$REMAINDERL_LIBM $ISNANL_LIBM" ;;
esac
fi
fi
--- modules/remainder.orig Sun Mar 4 22:56:29 2012
+++ modules/remainder Sun Mar 4 22:18:34 2012
@@ -8,8 +8,12 @@
Depends-on:
math
-round [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
-fma [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+isfinite [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+signbit [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+fabs [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+fmod [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+isnand [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
+isinf [test $HAVE_REMAINDER = 0 || test $REPLACE_REMAINDER = 1]
configure.ac:
gl_FUNC_REMAINDER
--- modules/remainderf.orig Sun Mar 4 22:56:29 2012
+++ modules/remainderf Sun Mar 4 22:55:25 2012
@@ -3,14 +3,19 @@
Files:
lib/remainderf.c
+lib/remainder.c
m4/remainderf.m4
m4/mathfunc.m4
Depends-on:
math
remainder [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
-roundf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
-fmaf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+isfinite [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+signbit [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+fabsf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+fmodf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+isnanf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
+isinf [test $HAVE_REMAINDERF = 0 || test $REPLACE_REMAINDERF = 1]
configure.ac:
gl_FUNC_REMAINDERF
--- modules/remainderl.orig Sun Mar 4 22:56:29 2012
+++ modules/remainderl Sun Mar 4 22:55:33 2012
@@ -3,14 +3,20 @@
Files:
lib/remainderl.c
+lib/remainder.c
m4/remainderl.m4
m4/mathfunc.m4
Depends-on:
math
remainder [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1]
-roundl [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
-fmal [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+float [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+isfinite [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+signbit [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+fabsl [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+fmodl [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+isnanl [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
+isinf [{ test $HAVE_REMAINDERL = 0 || test $REPLACE_REMAINDERL = 1;
} && test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 0]
configure.ac:
gl_FUNC_REMAINDERL
- fmod, fmodl: rewrite, Bruno Haible, 2012/03/04
- remainder, remainderf, remainderl: rewrite,
Bruno Haible <=