clang gets numerical underflow wrong, please fix.

Dimitry Andric dim at FreeBSD.org
Mon Mar 14 19:23:44 UTC 2016


On 14 Mar 2016, at 02:53, Steve Kargl <sgk at troutmask.apl.washington.edu> wrote:
...
> #include <fenv.h>
> #include <stdio.h>
> 
> int
> main(void)
> {
>   int i;
>   float x = 1.f;
>   i = 0;
>   feclearexcept(FE_ALL_EXCEPT);
>   do {
>      x *= 2;
>      i++;
>      printf("%d %e\n", i, x);
>   } while(!fetestexcept(FE_OVERFLOW));
>   if (fetestexcept(FE_OVERFLOW)) printf("FE_UNDERFLOW: ");
>   printf("x = %e after %d iterations\n", x, i);
> 
>   return 0;
> }
> 
> You'll get a bunch of invalid output before the OVERFLOW.
> 
> % cc -O -o z b.c -lm && ./z | tail
> 1016 7.022239e+305  <-- not a valid float
> 1017 1.404448e+306  <-- not a valid float
> 1018 2.808896e+306  <-- not a valid float
> 1019 5.617791e+306  <-- not a valid float
> 1020 1.123558e+307  <-- not a valid float
> 1021 2.247116e+307  <-- not a valid float
> 1022 4.494233e+307  <-- not a valid float
> 1023 8.988466e+307  <-- not a valid float
> 1024 inf
> FE_UNDERFLOW: x = inf after 1024 iterations
> 
> Clang is broken with or without #pragma FENV_ACCESS "on".

Well, it simply doesn't support that #pragma [1], just like gcc [2]. :-(

Apparently compiler writers have trouble with this pragma, don't
implement it, and assume that it's always off.  Which then appears to
make most (or all) fenv.h functions into undefined behavior.

That said, making 'x' in your test case volatile helps, e.g. the main
loop was:

        fadd    %st(0), %st(0)
        fstl    -20(%ebp)
        incl    %esi
        movl    %esi, 4(%esp)
        fstpl   8(%esp)
        movl    $.L.str, (%esp)
        calll   printf
        fnstsw  -10(%ebp)

and becomes:

        flds    -16(%ebp)
        fadd    %st(0), %st(0)
        fstps   -16(%ebp)
        incl    %esi
        flds    -16(%ebp)
        fstpl   8(%esp)
        movl    %esi, 4(%esp)
        movl    $.L.str, (%esp)
        calll   printf
        #APP
        fnstsw  -10(%ebp)

So the fstps causes an overflow when 128 iterations are reached:

[...]
126 8.507059e+37
127 1.701412e+38
128 inf
FE_UNDERFLOW: x = inf after 128 iterations

Maybe this is a usable workaround for libm.

-Dimitry

[1] https://llvm.org/bugs/show_bug.cgi?id=8100
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34678

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 194 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://lists.freebsd.org/pipermail/freebsd-toolchain/attachments/20160314/c08c1848/attachment.sig>


More information about the freebsd-toolchain mailing list