[PATCH] Don't call double rint from float powf.

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] Don't call double rint from float powf.

Jim Wilson-2
This is the first patch of two to fix problems with the powf code.  I haven't
written the other patch yet.

For targets that have only single float support, such as RISC-V rv32imafc, a
program using powf ends up pulling in soft-float adddf3 and subdf3 routines.
These come from the double rint call, which should be float rintf instead.
This makes a significant difference in the size of the resulting program.

I tested this with a simple testcase, with an rint function that calls abort
to verify that I'm testing the right code path.  Compiling with
"gcc -fno-builtin" I get for the patched and unpatched newlib

gamma02:2195$ ls -lt a.out*
-rwxrwxr-x 1 jimw jimw 47012 Dec 11 15:01 a.out
-rwxrwxr-x 1 jimw jimw 75680 Dec 11 14:46 a.out.unpatched
gamma02:2196$

So the result with the patch is about 60% of the size without the patch.  The
test program gives the correct result with the patch.  I also ran the newlib
make check, which passed with no regression, but I see it doesn't do much
useful.

        newlib/
        * libm/math/wf_pow.c (powf): Call rintf instead of rint.
---
 newlib/libm/math/wf_pow.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/newlib/libm/math/wf_pow.c b/newlib/libm/math/wf_pow.c
index be453558b..4ca0dc79d 100644
--- a/newlib/libm/math/wf_pow.c
+++ b/newlib/libm/math/wf_pow.c
@@ -127,11 +127,11 @@
     if (_LIB_VERSION == _SVID_) {
        exc.retval = HUGE;
        y *= 0.5;
-       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE;
+       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE;
     } else {
        exc.retval = HUGE_VAL;
                        y *= 0.5;
-       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
+       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
     }
     if (_LIB_VERSION == _POSIX_)
         errno = ERANGE;
--
2.14.1

Testcase:

#include <stdlib.h>
#include <math.h>

int
main (void)
{
  float f = powf (-2.0, -2.0);
  if (f != 0.25)
    abort ();

  float g = powf (-2.0, -3.0);
  if (g != -0.125)
    abort ();

  float a = powf (-2.0, 129);
  if (a != -__builtin_inff ())
    abort ();

  float b = powf (-2.0, 130);
  if (b != __builtin_inff ())
    abort ();

  return 0;
}

double
rint (double x)
{
  abort ();
}
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Don't call double rint from float powf.

Craig Howland
On 12/11/2017 07:08 PM, Jim Wilson wrote:

> This is the first patch of two to fix problems with the powf code.  I haven't
> written the other patch yet.
>
> For targets that have only single float support, such as RISC-V rv32imafc, a
> program using powf ends up pulling in soft-float adddf3 and subdf3 routines.
> These come from the double rint call, which should be float rintf instead.
> This makes a significant difference in the size of the resulting program.
>
> I tested this with a simple testcase, with an rint function that calls abort
> to verify that I'm testing the right code path.  Compiling with
> "gcc -fno-builtin" I get for the patched and unpatched newlib
>
> gamma02:2195$ ls -lt a.out*
> -rwxrwxr-x 1 jimw jimw 47012 Dec 11 15:01 a.out
> -rwxrwxr-x 1 jimw jimw 75680 Dec 11 14:46 a.out.unpatched
> gamma02:2196$
>
> So the result with the patch is about 60% of the size without the patch.  The
> test program gives the correct result with the patch.  I also ran the newlib
> make check, which passed with no regression, but I see it doesn't do much
> useful.
>
> newlib/
> * libm/math/wf_pow.c (powf): Call rintf instead of rint.
> ---
>   newlib/libm/math/wf_pow.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/newlib/libm/math/wf_pow.c b/newlib/libm/math/wf_pow.c
> index be453558b..4ca0dc79d 100644
> --- a/newlib/libm/math/wf_pow.c
> +++ b/newlib/libm/math/wf_pow.c
> @@ -127,11 +127,11 @@
>      if (_LIB_VERSION == _SVID_) {
>         exc.retval = HUGE;
>         y *= 0.5;
> -       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE;
> +       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE;
>      } else {
>         exc.retval = HUGE_VAL;
>                          y *= 0.5;
> -       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
> +       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
>      }
>      if (_LIB_VERSION == _POSIX_)
>          errno = ERANGE;
To be most rigorous, "0.0" on the same lines as the rintf() should be "0.0F" (as
otherwise the comparison strictly should be done in double).  (Not addressing
the exact change you are making, but is the same class of fix and are on the
same line of source code.)
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Don't call double rint from float powf.

Corinna Vinschen
On Dec 11 19:37, Craig Howland wrote:

> On 12/11/2017 07:08 PM, Jim Wilson wrote:
> > This is the first patch of two to fix problems with the powf code.  I haven't
> > written the other patch yet.
> >
> > For targets that have only single float support, such as RISC-V rv32imafc, a
> > program using powf ends up pulling in soft-float adddf3 and subdf3 routines.
> > These come from the double rint call, which should be float rintf instead.
> > This makes a significant difference in the size of the resulting program.
> >
> > I tested this with a simple testcase, with an rint function that calls abort
> > to verify that I'm testing the right code path.  Compiling with
> > "gcc -fno-builtin" I get for the patched and unpatched newlib
> >
> > gamma02:2195$ ls -lt a.out*
> > -rwxrwxr-x 1 jimw jimw 47012 Dec 11 15:01 a.out
> > -rwxrwxr-x 1 jimw jimw 75680 Dec 11 14:46 a.out.unpatched
> > gamma02:2196$
> >
> > So the result with the patch is about 60% of the size without the patch.  The
> > test program gives the correct result with the patch.  I also ran the newlib
> > make check, which passed with no regression, but I see it doesn't do much
> > useful.
> >
> > newlib/
> > * libm/math/wf_pow.c (powf): Call rintf instead of rint.
> > ---
> >   newlib/libm/math/wf_pow.c | 4 ++--
> >   1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/newlib/libm/math/wf_pow.c b/newlib/libm/math/wf_pow.c
> > index be453558b..4ca0dc79d 100644
> > --- a/newlib/libm/math/wf_pow.c
> > +++ b/newlib/libm/math/wf_pow.c
> > @@ -127,11 +127,11 @@
> >      if (_LIB_VERSION == _SVID_) {
> >         exc.retval = HUGE;
> >         y *= 0.5;
> > -       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE;
> > +       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE;
> >      } else {
> >         exc.retval = HUGE_VAL;
> >                          y *= 0.5;
> > -       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
> > +       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
> >      }
> >      if (_LIB_VERSION == _POSIX_)
> >          errno = ERANGE;
> To be most rigorous, "0.0" on the same lines as the rintf() should be "0.0F"
> (as otherwise the comparison strictly should be done in double).  (Not
> addressing the exact change you are making, but is the same class of fix and
> are on the same line of source code.)
Also, HUGE_VAL is double, so it should better be replaced with HUGE_VALF.


Thanks,
Corinna

--
Corinna Vinschen
Cygwin Maintainer
Red Hat

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Don't call double rint from float powf.

Jim Wilson-2
In reply to this post by Craig Howland
On 12/11/2017 04:37 PM, Craig Howland wrote:

> On 12/11/2017 07:08 PM, Jim Wilson wrote:
>> -               if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
>> +               if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
>>               }
>>               if (_LIB_VERSION == _POSIX_)
>>                   errno = ERANGE;
> To be most rigorous, "0.0" on the same lines as the rintf() should be
> "0.0F" (as otherwise the comparison strictly should be done in double).  
> (Not addressing the exact change you are making, but is the same class
> of fix and are on the same line of source code.)

Any reasonable compiler will get this right.  GCC does a float compare
here even if you compile without optimization.  So no fix here is
required, though I can redo the patch if you want.

Jim
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Don't call double rint from float powf.

Jim Wilson-2
In reply to this post by Corinna Vinschen
On 12/12/2017 04:48 AM, Corinna Vinschen wrote:

> On Dec 11 19:37, Craig Howland wrote:
>>> +       if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
>>>        }
>>>        if (_LIB_VERSION == _POSIX_)
>>>            errno = ERANGE;
>> To be most rigorous, "0.0" on the same lines as the rintf() should be "0.0F"
>> (as otherwise the comparison strictly should be done in double).  (Not
>> addressing the exact change you are making, but is the same class of fix and
>> are on the same line of source code.)
>
> Also, HUGE_VAL is double, so it should better be replaced with HUGE_VALF.

The "struct exception" type uses doubles, so this has to be HUGE_VAL.

Even with my two patches, we still have extendsfdf2 and truncdfsf2
calls, but those will be much harder to get rid of, as we can't change
struct exception without breaking matherr.  All of the other soft-float
calls are gone though.

Jim
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Don't call double rint from float powf.

Craig Howland
In reply to this post by Jim Wilson-2
On 12/12/2017 01:00 PM, Jim Wilson wrote:

> On 12/11/2017 04:37 PM, Craig Howland wrote:
>> On 12/11/2017 07:08 PM, Jim Wilson wrote:
>>> - if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
>>> +               if(x<0.0&&rintf(y)!=y) exc.retval = -HUGE_VAL;
>>>               }
>>>               if (_LIB_VERSION == _POSIX_)
>>>                   errno = ERANGE;
>> To be most rigorous, "0.0" on the same lines as the rintf() should be "0.0F"
>> (as otherwise the comparison strictly should be done in double).  (Not
>> addressing the exact change you are making, but is the same class of fix and
>> are on the same line of source code.)
>
> Any reasonable compiler will get this right.  GCC does a float compare here
> even if you compile without optimization.  So no fix here is required, though
> I can redo the patch if you want.
>
If you don't mind, it would be best that way, as it keeps the source compliant
with the C standard in terms of requiring a float comparison.  But it also could
be cleaned up later on, as I expect that there are some others that are the same
way that could also use it.

(It is an interesting thing to consider to decide if GCC is doing the right
thing here, or not.  While it knows that it is comparing against 0 and that
converting it from double 0 to float 0 has no runtime side effects, the user
could have purposely done it this way because they wanted to avoid a float
comparison.  I do agree that making it a float comparison is a good choice, just
that it could potentially be considered to be not in compliance with the standard.)
Reply | Threaded
Open this post in threaded view
|

[PATCH v2] Don't call double rint from float powf.

Jim Wilson-2
In reply to this post by Jim Wilson-2
Updated patch to use 0.0f in addition to calling rintf.

Tested same way as before, with a testcase that triggers the code and
make check.

OK?

        newlib/
        * libm/math/wf_pow.c (powf): Call rintf instead of rint.  Use 0.0f
        for compare.
---
 newlib/libm/math/wf_pow.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/newlib/libm/math/wf_pow.c b/newlib/libm/math/wf_pow.c
index be453558b..9a10254bf 100644
--- a/newlib/libm/math/wf_pow.c
+++ b/newlib/libm/math/wf_pow.c
@@ -127,11 +127,11 @@
     if (_LIB_VERSION == _SVID_) {
        exc.retval = HUGE;
        y *= 0.5;
-       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE;
+       if(x<0.0f&&rintf(y)!=y) exc.retval = -HUGE;
     } else {
        exc.retval = HUGE_VAL;
                        y *= 0.5;
-       if(x<0.0&&rint(y)!=y) exc.retval = -HUGE_VAL;
+       if(x<0.0f&&rintf(y)!=y) exc.retval = -HUGE_VAL;
     }
     if (_LIB_VERSION == _POSIX_)
         errno = ERANGE;
--
2.14.1

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] Don't call double rint from float powf.

Corinna Vinschen
On Dec 12 11:38, Jim Wilson wrote:

> Updated patch to use 0.0f in addition to calling rintf.
>
> Tested same way as before, with a testcase that triggers the code and
> make check.
>
> OK?
>
> newlib/
> * libm/math/wf_pow.c (powf): Call rintf instead of rint.  Use 0.0f
> for compare.
Pushed.  You can drop the old CVS ChangeLog entries btw., they
don't make sense anymore.


Thanks,
Corinna

--
Corinna Vinschen
Cygwin Maintainer
Red Hat

signature.asc (836 bytes) Download Attachment