]> sourceware.org Git - newlib-cygwin.git/commit
libm: Fixing overflow handling issue for scalbnf and scalbn
authorKito Cheng <kito.cheng@sifive.com>
Mon, 19 Jul 2021 08:50:22 +0000 (16:50 +0800)
committerCorinna Vinschen <corinna@vinschen.de>
Wed, 21 Jul 2021 07:56:04 +0000 (09:56 +0200)
commitca7b4bd23676a9cb6d82974c2ed452e08abb7ecf
tree6ede2d9e4b248c9e5796e2af172239d0f3d97706
parentff9c2b3ab996b359645954f22647eb1c60c9cefa
libm: Fixing overflow handling issue for scalbnf and scalbn

cc Aldy Hernandez <aldyh@redhat.com> and Andrew MacLeod <amacleod@redhat.com>,
they are author of new VRP analysis for GCC, just to make sure I didn't
mis-understanding or mis-interpreting anything on GCC site.

GCC 11 have better value range analysis, that give GCC more confidence
to perform more aggressive optimization, but it cause scalbn/scalbnf get
wrong result.

Using scalbn to demostrate what happened on GCC 11, see comments with VRP
prefix:

```c
double scalbn (double x, int n)
{
/* VRP RESULT: n = [-INF, +INF] */
        __int32_t  k,hx,lx;
        ...
        k = (hx&0x7ff00000)>>20;
/* VRP RESULT: k = [0, 2047] */
        if (k==0) {
    /* VRP RESULT: k = 0 */
    ...
    k = ((hx&0x7ff00000)>>20) - 54;
            if (n< -50000) return tiny*x;       /*underflow*/
    /* VRP RESULT: k = -54 */
}
/* VRP RESULT: k = [-54, 2047] */
        if (k==0x7ff) return x+x;               /* NaN or Inf */
/* VRP RESULT: k = [-54, 2046] */
        k = k+n;
        if (k > 0x7fe) return huge*copysign(huge,x); /* overflow  */
/* VRP RESULT: k = [-INF, 2046] */
/* VRP RESULT: n = [-INF, 2100],
   because k + n <= 0x7fe is false, so:
   1. -INF < [-54, 2046] + n <= 0x7fe(2046) < INF
   2. -INF < [-54, 2046] + n <= 2046 < INF
   3. -INF < n <= 2046 - [-54, 2046] < INF
   4. -INF < n <= [0, 2100] < INF
   5. n = [-INF, 2100] */
        if (k > 0)                              /* normal result */
            {SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); return x;}
        if (k <= -54) {
    /* VRP OPT: Evaluate n > 50000 as true...*/
            if (n > 50000)      /* in case integer overflow in n+k */
                return huge*copysign(huge,x);   /*overflow*/
            else return tiny*copysign(tiny,x);  /*underflow*/
}
        k += 54;                                /* subnormal result */
        SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20));
        return x*twom54;
}
```

However give the input n = INT32_MAX, k = k+n will overflow, and then we
expect got `huge*copysign(huge,x)`, but new VRP optimization think
`n > 50000` is never be true, so optimize that into `tiny*copysign(tiny,x)`.

so the solution here is to moving the overflow handle logic before `k = k + n`.
newlib/libm/common/s_scalbn.c
newlib/libm/common/sf_scalbn.c
This page took 0.030359 seconds and 5 git commands to generate.