On Fri, Dec 11, 2020 at 7:26 AM Xionghu Luo <luo...@linux.ibm.com> wrote:
>
> Thanks,
>
> On 2020/12/10 17:12, Richard Biener wrote:
> >> 2) From PR90070:
> >>
> >>    double temp1 = (double)r->red;
> >>    double temp2 = (double)aggregate.red;
> >>    double temp3 = temp2 + (temp1 * 5.0);
> > temp1 * 5 could be not representable in float but the
> > result of the add could so the transform could result
> > in -+Inf where the original computation was fine (but
> > still very large result).
> >
> > Usually in such cases one could say we should implement some
> > diagnostic hints to the user that he might consider refactoring
> > his code to use float computations because we cannot really say
> > whether it's safe (we do at the moment not implement value-range
> > propagation for floating point types).
> >
>
>    foo (double x, float y, float z)
>   {
>       return ( fabs (x) * y - z ) ;
>   }
>
>   int main ()
>   {
>     float res = foo (1e38, 5.0, 3e38);
>     printf ("res:%f\n", res);
>   }
>
> (1) $ gcc a.c -Ofast -ffp-contract=off:
>
> 0000000000000880 <foo>:
>  880:   10 0a 20 fc     fabs    f1,f1
>  884:   b2 00 21 fc     fmul    f1,f1,f2
>  888:   28 18 21 fc     fsub    f1,f1,f3
>  88c:   18 08 20 fc     frsp    f1,f1
>  890:   20 00 80 4e     blr
>
> $ ./a.out
> res:199999993605713849301312521538346418176.000000
>
> (2) $ gcc_MODIFIED a.c -Ofast -ffp-contract=off:
>
> 0000000010000660 <foo>:
>     10000660:   18 08 00 fc     frsp    f0,f1
>     10000664:   10 02 00 fc     fabs    f0,f0
>     10000668:   b2 00 00 ec     fmuls   f0,f0,f2   // Inf
>     1000066c:   28 18 20 ec     fsubs   f1,f0,f3
>     10000670:   20 00 80 4e     blr
>
> $ ./a.out
> res:inf
>
> It's true that if change all double computation to float will result
> in INF if "fabs (x) * y" is larger than FLT_MAX, though the double
> result in (1) could get back to a large number smaller than FLT_MAX.
>
>
> But the add/sub could also produces INF similarly,
>
>   foo (double x, float y, float z)
>   {
>      return ( -fabs (x) + y + z ) ;
>   }
>
>   int main ()
>   {
>      float res = foo (1e38, 1e38, 3e38);
>      printf ("res:%f\n", res);
>   }
>
> (3) $ gcc a.c -Ofast:
>
> 0000000000000880 <foo>:
>  880:   10 0a 20 fc     fabs    f1,f1
>  884:   28 08 42 fc     fsub    f2,f2,f1
>  888:   2a 18 22 fc     fadd    f1,f2,f3
>  88c:   18 08 20 fc     frsp    f1,f1
>  890:   20 00 80 4e     blr
>
> $ ./a.out
> res:300000000549775575777803994281145270272.000000
>
> 4) $ gcc_MODIFIED a.c -Ofast:
>
> 0000000010000660 <foo>:
>     10000660:   18 08 20 fc     frsp    f1,f1
>     10000664:   2a 18 42 ec     fadds   f2,f2,f3
>     10000668:   10 0a 20 fc     fabs    f1,f1
>     1000066c:   28 08 22 ec     fsubs   f1,f2,f1
>     10000670:   20 00 80 4e     blr
>
> $ ./a.out
> res:inf
>
>
> Note that the add/sub sequence is different for (3) and (4) since
> -funsafe-math-optimizations is implicitly true.  "fp-contract=fast" in
> (1) and (2) could avoid Inf as fmads could handle float overflow (verified
> it on Power, not sure other targets support this), but without float
> value-range info, it is unsafe to change computation from double to
> float even for only add/sub expressions.

Yes.  As said it's difficult to second guess the programmer here.
The existing cases doing promotion look at unary functions,
doing exp->expf when arguments are promoted floats and the
return value is casted back to float.  That's also a common programmer
"error" not knowing expf or assuming some kind of magic overloading.

So I'm not entirely convinced such transform is a good idea, at least
by default with -ffast-math.  Maybe have a -fassume-float-limited-range
or so documented as that we assume that double or long double values
used fit in floats?

Richard.

> What's more, It seems also difficult to do computation *partly float
> and partly double* in backprop pass since all the expressions are
> chained and strong dependent unlike the sign-changing operations,
> which could only change expressions partly.
>
>
> Thanks,
> Xionghu

Reply via email to