aboutsummaryrefslogtreecommitdiff
path: root/math/pow.c
diff options
context:
space:
mode:
authorSzabolcs Nagy <szabolcs.nagy@arm.com>2018-07-03 11:35:57 +0100
committerSzabolcs Nagy <szabolcs.nagy@arm.com>2018-07-03 12:59:23 +0100
commit2105bad5901834fa5ac38c16d8b6ce70a887caf4 (patch)
treee41407a9587c6402bc056a66daf3a791df1e592a /math/pow.c
parent58ce45c060855f525a26760a2aff22d30bbce7e9 (diff)
downloadarm-optimized-routines-2105bad5901834fa5ac38c16d8b6ce70a887caf4.tar.gz
Fix large ulp error in pow without fma very near 1.0
The !HAVE_FAST_FMA code path split r = z/c - 1 into r = rhi + rlo such that when z = 1-tiny and c = 1 then rlo and rhi could have much larger magnitude than r which later caused large rounding errors. So do a nearest rounding instead of truncation at the split.
Diffstat (limited to 'math/pow.c')
-rw-r--r--math/pow.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/math/pow.c b/math/pow.c
index d391509..ec9bc37 100644
--- a/math/pow.c
+++ b/math/pow.c
@@ -67,11 +67,13 @@ log_inline (uint64_t ix, double_t *tail)
logc = T[i].logc;
logctail = T[i].logctail;
- /* r = z/c - 1, arranged to be exact. */
+ /* Note: 1/c is j/N or j/N/2 where j is an integer in [N,2N) and
+ |z/c - 1| < 1/N, so r = z/c - 1 is exactly representible. */
#if HAVE_FAST_FMA
r = fma (z, invc, -1.0);
#else
- double_t zhi = asdouble (iz & (-1ULL << 32));
+ /* Split z such that rhi, rlo and rhi*rhi are exact and |rlo| <= |r|. */
+ double_t zhi = asdouble ((iz + (1ULL << 31)) & (-1ULL << 32));
double_t zlo = z - zhi;
double_t rhi = zhi * invc - 1.0;
double_t rlo = zlo * invc;