[dmd-internals] Pure CTFE-able std.math

Iain Buclaw ibuclaw at ubuntu.com
Mon Jul 15 09:46:57 PDT 2013


On 15 July 2013 15:04, Daniel Murphy <yebblies at gmail.com> wrote:
> I would prefer to see (as either builtins or properties) a way to get/set
> the exponent/mantissa/sign of a float.
>
> eg
> float x;
> x.sign = 1;
> assert(x.exp == 123);
>
> Easily ctfeable, no need for casting, no endianness problems.
>
> I expect everything could be implemented from these?
>
>

There's also, MANTISSA_LSB and MANTISSA_MSB are used to peek into
reals for 128bit real targets - but it can also apply to 80/96bit real
targets (lsb/msb being of 32bit integers instead of 64bit).

With floor, I've gone down the route of casting between real and
ushort[real.sizeof / short.sizeof] as ushort* alternative was being
optimised away by gcc backend.  :o)

I'd be happy to post it in all it's glory.  Please note that the other
functions implemented are not endianess specific (apart from the use
of isNaN, isInfinity, frexp).

------------
real ceil(real x)  @trusted pure nothrow
{
    // Special cases.
    if (isNaN(x) || isInfinity(x))
        return x;

    real y = floor(x);
    if (y < x)
        y += 1.0L;

    return y;
}

real floor(real x) @trusted pure nothrow
{
    alias floatTraits!(real) F;
    enum REALSZ = real.sizeof / ushort.sizeof;

    static if (real.mant_dig == 53)
    {
        version (LittleEndian)
            enum FRACTPOS_SHORT = 0;
        else
            enum FRACTPOS_SHORT = 3;
    }
    else static if (real.mant_dig == 64)
    {
        version (LittleEndian)
            enum FRACTPOS_SHORT = 0;
        else
            enum FRACTPOS_SHORT = 4;
    }
    else if (real.mant_dig == 113)
    {
        version (LittleEndian)
            enum FRACTPOS_SHORT = 0;
        else
            enum FRACTPOS_SHORT = 7;
    }
    else
        static assert(false, "Only 64-bit, 80-bit, and 128-bit reals
are supported");

    // Bit clearing masks.
    static immutable ushort[17] BMASK = [
        0xffff, 0xfffe, 0xfffc, 0xfff8,
        0xfff0, 0xffe0, 0xffc0, 0xff80,
        0xff00, 0xfe00, 0xfc00, 0xf800,
        0xf000, 0xe000, 0xc000, 0x8000,
        0x0000,
    ];

    // Special cases.
    if (isNaN(x) || isInfinity(x) || x == 0.0L)
        return x;

    // Find the exponent (power of 2)
    ushort[REALSZ] vu = *cast(ushort[REALSZ]*)&x;
    int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;

    if (exp < 0)
    {
        if (x < 0)
            return -1.0L;
        else
            return 0.0L;
    }

    int j = FRACTPOS_SHORT;
    exp = (real.mant_dig - 1) - exp;

    // Clean out 16 bits at a time.
    while (exp >= 16)
    {
        version (LittleEndian)
            vu[j++] = 0;
        else
            vu[j--] = 0;
        exp -= 16;
    }

    // Clear the remaining bits.
    if (exp > 0)
        vu[j] &= BMASK[exp];

    real y = *cast(real*)&vu;

    if ((x < 0.0L) && (y != x))
        y -= 1.0L;

    return y;
}


Regards
--
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';


More information about the dmd-internals mailing list