bad accuracy of powf for integer exponent
zimmermann6 opened this issue · comments
while doing some heuristic search for large errors (in ulps) of the single-precision powf function (with rounding to nearest) I noticed that for integer exponent we can get some quite bad accuracy.
One example is x=0xd.65874p-4f and y=4.0f, where I get an error of 1.9ulps with respect to the infinite precision result.
This is with OpenLibm 0.4.1 on x86_64 under Linux.
Is it possible for you to try it in the latest release? The library just takes a couple of mins to download and build and try out.
the last tar.gz available on https://openlibm.org/ is 0.4.1, this is the one I used
I don't that website has been updated in quite a while. You can get the 0.7.0 tarball from the GitHub releases page: https://github.com/JuliaMath/openlibm/archive/v0.7.0.tar.gz. You could also do a git clone
.
We should update the website. Thanks for bringing to our notice. Most people just download straight from GitHub.
Website updated.
@zimmermann6 can you tell us what value powf(x,y)
returned and the value you expect. powf
from FreeBSD matches the code in Openlibm. With MPFR set to 96-bit precision, I get
% ./testf -a 0xd.65874p-4f 4.0f libm = 4.91470873e-01f, /* 0x3efba212 */ mpfr = 4.91470873e-01f, /* 0x3efba212 */ ulp = 0.09839
Steve, here is what I get (same with 0.7.0):
#include <stdio.h>
#include <math.h>
int
main()
{
float x = 0xd.65874p-4f;
float y = 4.0f;
float z = powf (x, y);
printf ("z=%a\n", z);
}
$ gcc bug_openlibm.c /localdisk/zimmerma/openlibm-0.4.1/libopenlibm.a
$ ./a.out
z=0x1.f7442p-2
The expected value is 0x7.dd109p-4=0x1f74424p-2, which is the one I get with GNU libc.
I confirm this fixes the example I gave, now we get the same result as glibc. However for x=0x1.ffffeep-1 and y=-0x1.000002p+27 openlibm powf(x,y) gives +Inf whereas the correct result is 0x1.d53532p+103:
$ cat test_pow_openlibm.c
#include <stdio.h>
#include <math.h>
int main()
{
float x = 0x1.ffffeep-1f;
float y = -0x1.000002p+27f;
float z = powf (x, y);
printf ("x=%a y=%a z=%a\n", x, y, z);
}
$ gcc -fno-builtin test_pow_openlibm.c -lm
$ ./a.out
x=0x1.ffffeep-1 y=-0x1.000002p+27 z=0x1.d53532p+103
$ gcc -fno-builtin test_pow_openlibm.c /tmp/openlibm/libopenlibm.a
$ ./a.out
x=0x1.ffffeep-1 y=-0x1.000002p+27 z=inf
Should I open a separate issue for that?
Seems to be the same general issue so I think we can reopen this.
Ah well - this is most likely then an issue in the freebsd msun.
Interesting corner case x->1 and y >> 1. This patch, lacking any additional analysis, fixes the issue on FreeBSD.
Index: src/e_powf.c
===================================================================
--- src/e_powf.c (revision 2342)
+++ src/e_powf.c (working copy)
@@ -136,7 +136,7 @@ __ieee754_powf(float x, float y)
/* |y| is huge */
if(iy>0x4d000000) { /* if |y| > 2**27 */
/* over/underflow if x is not close to one */
- if(ix<0x3f7ffff8) return (hy<0)? sn*huge*huge:sn*tiny*tiny;
+ if(ix<0x3f7ffff7) return (hy<0)? sn*huge*huge:sn*tiny*tiny;
if(ix>0x3f800007) return (hy>0)? sn*huge*huge:sn*tiny*tiny;
/* now |1-x| is tiny <= 2**-20, suffice to compute
log(x) by x-x^2/2+x^3/3-x^4/4 */
I confirm this fixes the issue. Now the largest error I find with my program in a quick test is less than 1 ulp.
Thanks, committed in FreeBSD here: freebsd/freebsd-src@93fc678