aboutsummaryrefslogtreecommitdiff
path: root/math/powf.c
diff options
context:
space:
mode:
authorSzabolcs Nagy <szabolcs.nagy@arm.com>2018-12-07 13:47:14 +0000
committerSzabolcs Nagy <szabolcs.nagy@arm.com>2018-12-07 14:45:20 +0000
commit75b8d8c6c12b37a38211defcc5b941adb7de121f (patch)
treeafbc1a9594f364a3f74ab0b38383f10bb04376b1 /math/powf.c
parent11253b0b9d6ba14e5a609ff7436db1079c3f9bb8 (diff)
downloadarm-optimized-routines-75b8d8c6c12b37a38211defcc5b941adb7de121f.tar.gz
Fix powf overflow handling in non-nearest rounding mode
The threshold value at which powf overflows depends on the rounding mode and the current check did not take this into account. So when the result was rounded away from zero it could become infinity without setting errno to ERANGE. Example: pow(0x1.7ac7cp+5, 23) is 0x1.fffffep+127 + 0.1633ulp If the result goes above 0x1.fffffep+127 + 0.5ulp then errno is set, which is fine in nearest rounding mode, but powf(0x1.7ac7cp+5, 23) is inf in upward rounding mode powf(-0x1.7ac7cp+5, 23) is -inf in downward rounding mode and the previous implementation did not set errno in these cases. This special case is fixed without affecting the common code path by checking the rounding mode and using appropriate threshold value. Arithmetics is used to check the rounding mode to avoid introducing a stack frame when calling fegetround. Unfortunately the current test system does not support rounding modes. (Found by comparing against musl powf behaviour on the libc-test test suite, unfortunately that test system does not support errno.)
Diffstat (limited to 'math/powf.c')
-rw-r--r--math/powf.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/math/powf.c b/math/powf.c
index af51adb..06116f0 100644
--- a/math/powf.c
+++ b/math/powf.c
@@ -199,7 +199,14 @@ powf (float x, float y)
{
/* |y*log(x)| >= 126. */
if (ylogx > 0x1.fffffffd1d571p+6 * POWF_SCALE)
+ /* |x^y| > 0x1.ffffffp127. */
return __math_oflowf (sign_bias);
+ if (WANT_ROUNDING && WANT_ERRNO
+ && ylogx > 0x1.fffffffa3aae2p+6 * POWF_SCALE)
+ /* |x^y| > 0x1.fffffep127, check if we round away from 0. */
+ if ((!sign_bias && opt_barrier_float (-ylogx) != -128 * POWF_SCALE)
+ || (sign_bias && -opt_barrier_float (ylogx) != -128 * POWF_SCALE))
+ return __math_oflowf (sign_bias);
if (ylogx <= -150.0 * POWF_SCALE)
return __math_uflowf (sign_bias);
#if WANT_ERRNO_UFLOW