fenv.h fixes for softfloat

Ian Lepore freebsd at damnhippie.dyndns.org
Mon Jan 16 16:15:56 UTC 2012

On Sun, 2012-01-15 at 21:26 -0500, David Schultz wrote:
> On Sat, Jan 14, 2012, Ian Lepore wrote:
> I believe 0x10 is FE_INEXACT on arm, not FE_INVALID.  FE_INEXACT
> is correct here, because w+TWO52[sx] is inexact.  That still
> doesn't explain the lrint() failures, but it makes them less
> mysterious.  For instance, the implementation of the cast from
> double to int could raise a bogus inexact exception when the
> number is an integer to begin with.  In practice, I think the
> emulated casts don't raise the proper exceptions even when they
> should.  It might depend on whether it's linking against libgcc's
> __fixdfsi() or libc's.

Doh!  I could've sworn at one point I was seeing FE_INVALID set after
the cast, but now the only thing I'm really sure of is that in jumping
from one theory and way of testing it to another I seem to have
thoroughly confused myself (and probably you too, sorry about that).

So, on theory that there's probably nothing wrong with rint() but maybe
something wrong with casting, I tried a test that does what the guts of
llrint() does, but breaking the casting into a separate step and looking
at the flags after each operation.  I also tried casting to (long)
versus (long long) and the results are different.  Here's the broken
down test code:

        fenv_t env;
        volatile long long llr;
        volatile double dr;
        volatile double _d = 1.0;
        printf("except 1: %#x\n", fetestexcept(FE_ALL_EXCEPT));
        dr = rint(_d);
        printf("except 2: %#x\n", fetestexcept(FE_ALL_EXCEPT));
        if (fetestexcept(FE_INVALID))
        printf("except 3: %#x\n", fetestexcept(FE_ALL_EXCEPT));
        llr = (long)dr;
        printf("except 4: %#x\n", fetestexcept(FE_ALL_EXCEPT));
        llr = (long long)dr;
        printf("except 5: %#x\n", fetestexcept(FE_ALL_EXCEPT));
        printf("except 6: %#x _d=%f dr=%f llr=%lld\n", fetestexcept(FE_ALL_EXCEPT), _d, dr, llr);

And here's the output from running it:

        except 1: 0
        except 2: 0
        except 3: 0
        except 4: 0
        except 5: 0x10
        except 6: 0x10 _d=1.000000 dr=1.000000 llr=1

It's interesting that a cast to 32 bits is fine but a cast directly to
64 bits raises FE_INEXACT for the same value.

I did spot-check a few values other than 1.0, and they behaved the same
(IE, 1.0 is not special, it happens with anything.0), and of course with
non-integer starting values FE_INEXACT is set coming out of rint() as
you'd expect.

BTW, I never reposted the results of running all the tests after fixing
the long double printf glitch, but basically it looked like they were
identical to the prior results except that the values were printedly
correctly.  That is, it appears that everything that was failing still
failed, but I'm going to confirm that more rigorously soon (like making
sure I don't have any forgotten hacks/tweaks in any lib or test code).

-- Ian

More information about the freebsd-arm mailing list