Jump to page: 1 2
Thread overview
[dmd-internals] Pure CTFE-able std.math
Jul 15, 2013
Iain Buclaw
Jul 15, 2013
Iain Buclaw
Jul 15, 2013
Daniel Murphy
Jul 15, 2013
H. S. Teoh
Jul 16, 2013
Don Clugston
Jul 15, 2013
Iain Buclaw
Jul 15, 2013
Don Clugston
Jul 15, 2013
Iain Buclaw
Jul 16, 2013
Don Clugston
Jul 16, 2013
Iain Buclaw
Jul 16, 2013
Iain Buclaw
July 15, 2013
Don,

I'm in the middle of doing pure real implementations of the elementary math functions in std.math in D for the @safe/pure/nothrow routines that call impure extern(C) mathlib as a fallback if D_InlineAsm is not defined.

One nice side effect of this is that the functions are now CTFE-able - well... almost. :o)

The current blocker at the moment where CTFE support would be greatly appreciated is in the functions that require bit set/testing.  In particular: isInfinity, floor, and ceil.

With isInfinity, could perhaps do an alternate test:

if (__ctfe)
  return (x / 2) == x;


But for floor and ceil, at least one of the following must be available in CTFE to allow setting bits.

----
// Cannot convert &real to ushort* at compile time
real y = x;
ushort* sh = cast(ushort*)&y;
----

----
// Cannot convert &real to ushort[8LU]* at compile time
enum RSIZE_SHORT = real.sizeof/ushort.sizeof;
ushort[RSIZE_SHORT] sh = *cast(ushort[RSIZE_SHORT]*)&x;  // _d_arraycopy
/* ... */
// reinterpreting cast from immutable(ushort[8LU]) to real* is not
supported in CTFE
real y = *cast(real*)&sh;
----

----
// Unions with overlapping fields are not yet supported in CTFE
union U
{
    real y;
    ushort[RSIZE_SHORT] sh;
}
U u = { x };
----


Now, the pressing question is, which method do you prefer / would like to see in CTFE first. :o)


Thanks,
--
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

July 15, 2013
On 15 July 2013 10:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
>
> Now, the pressing question is, which method do you prefer / would like to see in CTFE first. :o)
>

Alternatively, we could add BUILTINfloor and BUILTINceil into builtins.c, but I'd rather avoid this route if possible. :-)

--
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

July 16, 2013
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?


On Mon, Jul 15, 2013 at 8:22 PM, Iain Buclaw <ibuclaw@ubuntu.com> wrote:

> On 15 July 2013 10:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
> >
> > Now, the pressing question is, which method do you prefer / would like to see in CTFE first. :o)
> >
>
> Alternatively, we could add BUILTINfloor and BUILTINceil into builtins.c, but I'd rather avoid this route if possible. :-)
>
> --
> Iain Buclaw
>
> *(p < e ? p++ : p) = (c & 0x0f) + '0';
> _______________________________________________
> dmd-internals mailing list
> dmd-internals@puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-internals
>


July 15, 2013
On Tue, Jul 16, 2013 at 12:04:08AM +1000, Daniel Murphy 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.
[...]

+1. IMO this is the right way to handle floating-point attributes. Direct knowledge about the binary representation should be restricted to the compiler as much as possible. Plus, having these attributes could potentially be useful in other contexts than std.math.


T

-- 
The best way to destroy a cause is to defend it poorly.
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

July 15, 2013
On 15 July 2013 11:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:

> But for floor and ceil, at least one of the following must be available in CTFE to allow setting bits.
>
> ----
> // Cannot convert &real to ushort* at compile time
> real y = x;
> ushort* sh = cast(ushort*)&y;
>


Yeah. The problem with this, is that what happens if somebody stores the pointer? It introduces loads of special cases.

The most minimal solution would be to explicitly allow:

ushort e = *(cast ushort *)&y;
*(cast ushort *)&y = e;

and likewise for getting the raw mantissa into a ulong.
Simply two permissible reads, and two permissible writes, and only for x86.
Essentially provides .exp | sign, and .mantissa as writable properties, but
without syntax sugar. We could wrap it in a library to create syntax sugar.

It's really a hack, but this is one of those low-level primitives that
needs to be provided as a special case, it's kind of an __asm feature.
I think the special case nature of this is unavoidable, it creates a host
of problems if you allow general casting.

We need it for doing atof() at compile-time, too, so providing built-in ceil and floor is not an option.


July 15, 2013
On 15 July 2013 15:04, Daniel Murphy <yebblies@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';
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

July 15, 2013
On 15 July 2013 16:22, Don Clugston <dclugston@gmail.com> wrote:
>
>
> On 15 July 2013 11:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
>>
>> But for floor and ceil, at least one of the following must be available in CTFE to allow setting bits.
>>
>> ----
>> // Cannot convert &real to ushort* at compile time
>> real y = x;
>> ushort* sh = cast(ushort*)&y;
>
>
>
> Yeah. The problem with this, is that what happens if somebody stores the pointer? It introduces loads of special cases.
>
> The most minimal solution would be to explicitly allow:
>
> ushort e = *(cast ushort *)&y;
> *(cast ushort *)&y = e;
>

Hmm, how about static arrays of the same size?  This is safer because of copy semantics (e is a copy of y).

ushort[8] e = *cast(ushort[8]*)&y;
y = *cast(real *)&e;


--
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

July 16, 2013
On 15 July 2013 16:54, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:

> On Tue, Jul 16, 2013 at 12:04:08AM +1000, Daniel Murphy 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.
> [...]
>
> +1. IMO this is the right way to handle floating-point attributes. Direct knowledge about the binary representation should be restricted to the compiler as much as possible. Plus, having these attributes could potentially be useful in other contexts than std.math.
>

No. You shouldn't be doing these kind of operations in normal code, you
should be using normal arithmetic. If you need to know the exponent, you
should use library functions like frexp().
You shouldn't be poking in the internals of a float unless you know the bit
pattern exactly, and you need the extra speed.
This issue only applies to 80-bit reals on x86, this is really a very
special case. Floats and doubles already allow the necessary casts.







>
>
> T
>
> --
> The best way to destroy a cause is to defend it poorly.
> _______________________________________________
> dmd-internals mailing list
> dmd-internals@puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-internals
>


July 16, 2013
On 15 July 2013 18:50, Iain Buclaw <ibuclaw@ubuntu.com> wrote:

> On 15 July 2013 16:22, Don Clugston <dclugston@gmail.com> wrote:
> >
> >
> > On 15 July 2013 11:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
> >>
> >> But for floor and ceil, at least one of the following must be available in CTFE to allow setting bits.
> >>
> >> ----
> >> // Cannot convert &real to ushort* at compile time
> >> real y = x;
> >> ushort* sh = cast(ushort*)&y;
> >
> >
> >
> > Yeah. The problem with this, is that what happens if somebody stores the pointer? It introduces loads of special cases.
> >
> > The most minimal solution would be to explicitly allow:
> >
> > ushort e = *(cast ushort *)&y;
> > *(cast ushort *)&y = e;
> >
>
> Hmm, how about static arrays of the same size?  This is safer because of copy semantics (e is a copy of y).
>
> ushort[8] e = *cast(ushort[8]*)&y;
> y = *cast(real *)&e;
>

True. But I don't think you would ever write runtime code that way, it
seems quite unnatural since it is so slow.
Could certainly be done.


July 16, 2013
On 16 July 2013 08:12, Don Clugston <dclugston@gmail.com> wrote:
> On 15 July 2013 18:50, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
>>
>> On 15 July 2013 16:22, Don Clugston <dclugston@gmail.com> wrote:
>> >
>> >
>> > On 15 July 2013 11:46, Iain Buclaw <ibuclaw@ubuntu.com> wrote:
>> >>
>> >> But for floor and ceil, at least one of the following must be available in CTFE to allow setting bits.
>> >>
>> >> ----
>> >> // Cannot convert &real to ushort* at compile time
>> >> real y = x;
>> >> ushort* sh = cast(ushort*)&y;
>> >
>> >
>> >
>> > Yeah. The problem with this, is that what happens if somebody stores the pointer? It introduces loads of special cases.
>> >
>> > The most minimal solution would be to explicitly allow:
>> >
>> > ushort e = *(cast ushort *)&y;
>> > *(cast ushort *)&y = e;
>> >
>>
>> Hmm, how about static arrays of the same size?  This is safer because of copy semantics (e is a copy of y).
>>
>> ushort[8] e = *cast(ushort[8]*)&y;
>> y = *cast(real *)&e;
>
>
> True. But I don't think you would ever write runtime code that way, it seems
> quite unnatural since it is so slow.
> Could certainly be done.
>

I wouldn't have thought it would be much different to using a union.

--
Iain Buclaw

*(p < e ? p++ : p) = (c & 0x0f) + '0';
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

« First   ‹ Prev
1 2