summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordas <das@FreeBSD.org>2005-03-18 02:27:59 +0000
committerdas <das@FreeBSD.org>2005-03-18 02:27:59 +0000
commit79b831e3a10eb4279659d3c557f0534edac9696e (patch)
treede0cb820607fd6c292ee8c0e14ef31f173c3268c
parentd2a9f37c64b7439883ebdc747907c8e911e96b97 (diff)
downloadFreeBSD-src-79b831e3a10eb4279659d3c557f0534edac9696e.zip
FreeBSD-src-79b831e3a10eb4279659d3c557f0534edac9696e.tar.gz
Fix the double rounding problem with subnormals, and
remove the XXX comments, which no longer apply.
-rw-r--r--lib/msun/src/s_fma.c30
-rw-r--r--lib/msun/src/s_fmal.c22
2 files changed, 36 insertions, 16 deletions
diff --git a/lib/msun/src/s_fma.c b/lib/msun/src/s_fma.c
index 5403070..c6d27bd 100644
--- a/lib/msun/src/s_fma.c
+++ b/lib/msun/src/s_fma.c
@@ -45,15 +45,8 @@ __FBSDID("$FreeBSD$");
* to be stored in FP registers in order to avoid incorrect results.
* This is the default on FreeBSD, but not on many other systems.
*
- * Tests on an Itanium 2 indicate that the hardware's FMA instruction
- * is almost twice as fast as this implementation. The hardware
- * instruction should be used on platforms that support it.
- *
- * XXX May incur an absolute error of 0x1p-1074 for subnormal results
- * due to double rounding induced by the final scaling operation.
- *
- * XXX On machines supporting quad precision, we should use that, but
- * see the caveat in s_fmaf.c.
+ * Hardware instructions should be used on architectures that support it,
+ * since this implementation will likely be several times slower.
*/
#if LDBL_MANT_DIG != 113
double
@@ -174,8 +167,23 @@ fma(double x, double y, double z)
s = r - c;
rr = (c - (r - s)) + (zs - s) + cc;
- fesetround(oround);
- return (ldexp(r + rr, ex + ey));
+ spread = ex + ey;
+ if (spread + ilogb(r) > -1023) {
+ fesetround(oround);
+ r = r + rr;
+ } else {
+ /*
+ * The result is subnormal, so we round before scaling to
+ * avoid double rounding.
+ */
+ p = ldexp(copysign(0x1p-1022, r), -spread);
+ c = r + p;
+ s = c - r;
+ cc = (r - (c - s)) + (p - s) + rr;
+ fesetround(oround);
+ r = (c + cc) - p;
+ }
+ return (ldexp(r, spread));
}
#else /* LDBL_MANT_DIG == 113 */
/*
diff --git a/lib/msun/src/s_fmal.c b/lib/msun/src/s_fmal.c
index c1b6413..1604778 100644
--- a/lib/msun/src/s_fmal.c
+++ b/lib/msun/src/s_fmal.c
@@ -39,9 +39,6 @@ __FBSDID("$FreeBSD$");
*
* Dekker, T. A Floating-Point Technique for Extending the
* Available Precision. Numer. Math. 18, 224-242 (1971).
- *
- * XXX May incur a small error for subnormal results due to double
- * rounding induced by the final scaling operation.
*/
long double
fmal(long double x, long double y, long double z)
@@ -165,6 +162,21 @@ fmal(long double x, long double y, long double z)
s = r - c;
rr = (c - (r - s)) + (zs - s) + cc;
- fesetround(oround);
- return (ldexpl(r + rr, ex + ey));
+ spread = ex + ey;
+ if (spread + ilogbl(r) > -16383) {
+ fesetround(oround);
+ r = r + rr;
+ } else {
+ /*
+ * The result is subnormal, so we round before scaling to
+ * avoid double rounding.
+ */
+ p = ldexpl(copysignl(0x1p-16382L, r), -spread);
+ c = r + p;
+ s = c - r;
+ cc = (r - (c - s)) + (p - s) + rr;
+ fesetround(oround);
+ r = (c + cc) - p;
+ }
+ return (ldexpl(r, spread));
}
OpenPOWER on IntegriCloud