summaryrefslogtreecommitdiffstats
path: root/lib/msun/src/s_fmal.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/msun/src/s_fmal.c')
-rw-r--r--lib/msun/src/s_fmal.c22
1 files changed, 17 insertions, 5 deletions
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