Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bad accuracy of powf for integer exponent #211

Closed
zimmermann6 opened this issue Aug 28, 2020 · 13 comments
Closed

bad accuracy of powf for integer exponent #211

zimmermann6 opened this issue Aug 28, 2020 · 13 comments

Comments

@zimmermann6
Copy link

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.

@ViralBShah
Copy link
Member

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.

@zimmermann6
Copy link
Author

the last tar.gz available on https://openlibm.org/ is 0.4.1, this is the one I used

@ararslan
Copy link
Member

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.

@ViralBShah
Copy link
Member

We should update the website. Thanks for bringing to our notice. Most people just download straight from GitHub.

@ViralBShah
Copy link
Member

Website updated.

@kargl
Copy link
Contributor

kargl commented Aug 30, 2020

@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

@zimmermann6
Copy link
Author

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.

ViralBShah pushed a commit that referenced this issue Feb 6, 2021
Patched by importing latest msun version
ViralBShah added a commit that referenced this issue Feb 6, 2021
@zimmermann6
Copy link
Author

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?

@ararslan
Copy link
Member

ararslan commented Feb 7, 2021

Seems to be the same general issue so I think we can reopen this.

@ararslan ararslan reopened this Feb 7, 2021
@ViralBShah
Copy link
Member

Ah well - this is most likely then an issue in the freebsd msun.

@kargl
Copy link
Contributor

kargl commented Feb 7, 2021

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 */

@zimmermann6
Copy link
Author

I confirm this fixes the issue. Now the largest error I find with my program in a quick test is less than 1 ulp.

ViralBShah pushed a commit that referenced this issue Feb 8, 2021
Co-authored by: @kargl
@DimitryAndric
Copy link

Thanks, committed in FreeBSD here: freebsd/freebsd-src@93fc678

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants