summaryrefslogtreecommitdiff
path: root/lib/remainderf.c
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2012-02-26 00:36:01 +0100
committerBruno Haible <bruno@clisp.org>2012-02-26 00:36:01 +0100
commit0e1c6ff93f27c939ba9e0df945b16ef98eaaeef1 (patch)
treeef99348fd84d3144205514baa0e384464cfc9e8a /lib/remainderf.c
parent87fdcafc0dd23b672f8d9496625b6c04dce1e324 (diff)
downloadgnulib-0e1c6ff93f27c939ba9e0df945b16ef98eaaeef1.tar.gz
fmodl, remainder*: Avoid wrong results due to rounding errors.
* lib/fmodl.c (fmodl): Correct the result if it is not within the expected bounds. * lib/remainderf.c (remainderf): Likewise. * lib/remainder.c (remainder): Likewise. * lib/remainderl.c (remainderl): Likewise.
Diffstat (limited to 'lib/remainderf.c')
-rw-r--r--lib/remainderf.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/lib/remainderf.c b/lib/remainderf.c
index 5d9ddb8127..d75cf2d39e 100644
--- a/lib/remainderf.c
+++ b/lib/remainderf.c
@@ -25,7 +25,26 @@ remainderf (float x, float y)
#if HAVE_REMAINDER
return (float) remainder ((double) x, (double) y);
#else
- float i = roundf (x / y);
- return fmaf (- i, y, x);
+ 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;
#endif
}