December 13, 2012
On 2012-51-13 13:12, kenji hara <k.hara.pg@gmail.com> wrote:

> D does not support such implicit *construction* in return statement and
> function argument.
> It is a current language design, and not a bug.

Walter does not seem to agree (see his post in this discussion). Previous
discussions with Andrei (see bug #8570[1] and related discussion[2])
indicates he also thinks alias this (or some other language feature)
should support this. I guess this is not really relevant to this
discussion, so I'll make a separate  thread.


[1]: http://d.puremagic.com/issues/show_bug.cgi?id=8570
[2]: http://forum.dlang.org/thread/sedknwtlaefrxuflnbez@forum.dlang.org?page=8#postjul0qv:242l9d:241:40digitalmars.com


-- 
Simen
December 14, 2012
On 12/13/2012 6:30 AM, Simen Kjaeraas wrote:
> Walter does not seem to agree (see his post in this discussion).

Note that the following implementation of halffloat does work, allowing explicit cast to halffloat and implicit conversion from.
(The halffloat literals don't work at the moment because of a limitation in CTFE, I'm working with Don to resolve that.)
-----------------------------------------------------------

/*
 * References:
 *      http://en.wikipedia.org/wiki/Half-precision_floating-point_format
 */

module halffloat;

struct HF {

    /* Provide implicit conversion of HF to float
     */

    @property float toFloat() { return shortToFloat(s); }
    alias toFloat this;

    /* Done as a template in order to prevent implicit conversion
     * of argument to float.
     */

    this(T : float)(T f)
    {
        static assert(is(T == float));
        s = floatToShort(f);
    }

    /* These are done as properties to avoid
     * circular reference problems.
     */

    static @property HF min_normal() { HF hf = void; hf.s = 0x0400; return hf; /* fp16!0x1p-14; */ }
    static @property HF max()        { HF hf = void; hf.s = 0x7BFF; return hf; /* fp16!0x1.FFCp+15; */ }
    static @property HF nan()        { HF hf = void; hf.s = EXPMASK | 1; return hf; /* fp16!(float.nan); */ }
    static @property HF infinity()   { HF hf = void; hf.s = EXPMASK; return hf; /* fp16!(float.infinity); */ }
    static @property HF epsilon()    { HF hf = void; hf.s = 0x3C01; return hf; /* fp16!0x1p-10; */ }

    enum dig =        3;
    enum mant_dig =   11;
    enum max_10_exp = 5;
    enum max_exp =    16;
    enum min_10_exp = -5;
    enum min_exp =    -14;

  private:
    ushort s = EXPMASK | 1;     // .init is HF.nan
}

/********************
 * User defined literal for Half Float.
 */

template fp16(float v)
{
    enum fp16 = HF(v);
}

private:

// Half float values
enum SIGNMASK  = 0x8000;
enum EXPMASK   = 0x7C00;
enum MANTMASK  = 0x03FF;
enum HIDDENBIT = 0x0400;

// float values
enum FSIGNMASK  = 0x80000000;
enum FEXPMASK   = 0x7F800000;
enum FMANTMASK  = 0x007FFFFF;
enum FHIDDENBIT = 0x00800000;

// Rounding mode
enum ROUND { TONEAREST, UPWARD, DOWNWARD, TOZERO };
enum ROUNDMODE = ROUND.TONEAREST;

union U { uint u; float f; }

ushort floatToShort(float f)
{
    /* If the target CPU has a conversion instruction, this code could be
     * replaced with inline asm or a compiler intrinsic, but leave this
     * as the CTFE path so CTFE can work on it.
     */

    /* The code currently does not set INEXACT, UNDERFLOW, or OVERFLOW,
     * but is marked where those would go.
     */

    U uf = void;
    uf.f = f;
    uint s = uf.u;

    ushort u = (s & FSIGNMASK) ? SIGNMASK : 0;
    int exp = s & FEXPMASK;
    if (exp == FEXPMASK)  // if nan or infinity
    {
        if ((s & FMANTMASK) == 0)       // if infinity
        {
            u |= EXPMASK;
        }
        else                            // else nan
        {
            u |= EXPMASK | 1;
        }
        return u;
    }

    uint significand = s & FMANTMASK;

    if (exp == 0)                       // if subnormal or zero
    {
        if (significand == 0)           // if zero
            return u;

        /* A subnormal float is going to give us a zero result anyway,
         * so just set UNDERFLOW and INEXACT and return +-0.
         */
        return u;
    }
    else                                // else normal
    {
        // normalize exponent and remove bias
        exp = (exp >> 23) - 127;
        significand |= FHIDDENBIT;
    }

    exp += 15;                          // bias the exponent

    bool guard = false;                 // guard bit
    bool sticky = false;                // sticky bit

    uint shift = 13;                    // lop off rightmost 13 bits
    if (exp <= 0)                       // if subnormal
    {   shift += -exp + 1;              // more bits to lop off
        exp = 0;
    }
    if (shift > 23)
    {
        // Set UNDERFLOW, INEXACT, return +-0
        return u;
    }

    // Lop off rightmost 13 bits, but save guard and sticky bits
    guard = (significand & (1 << (shift - 1))) != 0;
    sticky = (significand & ((1 << (shift - 1)) - 1)) != 0;
    significand >>= shift;

    if (guard || sticky)
    {
        // Lost some bits, so set INEXACT and round the result
        switch (ROUNDMODE)
        {
            case ROUND.TONEAREST:
                if (guard && (sticky || (significand & 1)))
                    ++significand;
                break;

            case ROUND.UPWARD:
                if (!(s & FSIGNMASK))
                    ++significand;
                break;

            case ROUND.DOWNWARD:
                if (s & FSIGNMASK)
                    ++significand;
                break;

            case ROUND.TOZERO:
                break;

            default:
                assert(0);
        }
        if (exp == 0)                           // if subnormal
        {
            if (significand & HIDDENBIT)        // and not a subnormal no more
                ++exp;
        }
        else if (significand & (HIDDENBIT << 1))
        {
            significand >>= 1;
            ++exp;
        }
    }

    if (exp > 30)
    {   // Set OVERFLOW and INEXACT, return +-infinity
        return u | EXPMASK;
    }

    /* Add exponent and significand into result.
     */

    u |= exp << 10;                             // exponent
    u |= (significand & ~HIDDENBIT);            // significand

    return u;
}

float shortToFloat(ushort s)
{
    /* If the target CPU has a conversion instruction, this code could be
     * replaced with inline asm or a compiler intrinsic, but leave this
     * as the CTFE path so CTFE can work on it.
     */
    /* This one is fairly easy because there are no possible errors
     * and no necessary rounding.
     */

    int exp = s & EXPMASK;
    if (exp == EXPMASK)  // if nan or infinity
    {
        float f;
        if ((s & MANTMASK) == 0)        // if infinity
        {
            f = float.infinity;
        }
        else                            // else nan
        {
            f = float.nan;
        }
        return (s & SIGNMASK) ? -f : f;
    }

    uint significand = s & MANTMASK;

    if (exp == 0)                       // if subnormal or zero
    {
        if (significand == 0)           // if zero
            return (s & SIGNMASK) ? -0.0f : 0.0f;

        // Normalize by shifting until the hidden bit is 1
        while (!(significand & HIDDENBIT))
        {
            significand <<= 1;
            --exp;
        }
        significand &= ~HIDDENBIT;      // hidden bit is, well, hidden
	exp -= 14;
    }
    else                                // else normal
    {
        // normalize exponent and remove bias
        exp = (exp >> 10) - 15;
    }

    /* Assemble sign, exponent, and significand into float.
     * Don't have to deal with overflow, inexact, or subnormal
     * because the range of floats is big enough.
     */

    assert(-126 <= exp && exp <= 127);  // just to be sure

    //printf("exp = %d, significand = x%x\n", exp, significand);

    uint u = (s & SIGNMASK) << 16;      // sign bit
    u |= (exp + 127) << 23;             // bias the exponent and shift into position
    u |= significand << (23 - 10);

    U uf = void;

    uf.u = u;
    return uf.f;
}

import std.stdio;

void main() {
//    HF h = fp16!27.2f;
//    HF j = cast(HF)( fp16!3.5f + fp16!5 );
    HF f = HF(0.0f);

    f.s = 0x3C00;
    writeln("1 ", cast(float)f);

    f.s = 0x3C01;
    writeln("1.0009765625 ", cast(float)f);
    assert(f == HF.epsilon);

    f.s = 0xC000;
    writeln("-2 ", cast(float)f);

    f.s = 0x7BFF;
    writeln("65504 ", cast(float)f);
    assert(f == HF.max);

    f.s = 0x0400;
    writeln("6.10352e-5 ", cast(float)f);
    assert(f == HF.min_normal);

    f.s = 0x03FF;
    writeln("6.09756e-5 ", cast(float)f);

    f.s = 1;
    writeln("5.96046e-8 ", cast(float)f);

    f.s = 0;
    writeln("0 ", cast(float)f);
    assert(f == 0.0f);

    f.s = 0x8000;
    writeln("-0 ", cast(float)f);
    assert(f == -0.0f);

    f.s = 0x7C00;
    writeln("infinity ", cast(float)f);
    assert(f == HF.infinity);

    f.s = 0xFC00;
    writeln("-infinity ", cast(float)f);
    assert(f == -HF.infinity);

    f.s = 0x3555;
    writeln("0.33325 ", cast(float)f);
}

December 14, 2012
On Thu, Dec 13, 2012 at 05:44:23PM -0800, Walter Bright wrote:
> On 12/13/2012 6:30 AM, Simen Kjaeraas wrote:
> >Walter does not seem to agree (see his post in this discussion).
> 
> Note that the following implementation of halffloat does work, allowing explicit cast to halffloat and implicit conversion from. (The halffloat literals don't work at the moment because of a limitation in CTFE, I'm working with Don to resolve that.)
> -----------------------------------------------------------

Nice!!


[...]
>     /* Provide implicit conversion of HF to float
>      */
> 
>     @property float toFloat() { return shortToFloat(s); }
>     alias toFloat this;
[...]

This is cool, I've never thought of using alias this on a @property function. I'll have to start using that in my code. :-)

The rest of the code is tl;dr, but I think it does showcase quite well the power of user-defined types in D. Now we just have to iron out implicit conversion from literals (and I mean that in general, not just for this particular example), and it will just be great.


T

-- 
Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
December 14, 2012
On 12/13/2012 10:19 PM, H. S. Teoh wrote:
> This is cool, I've never thought of using alias this on a @property
> function. I'll have to start using that in my code. :-)

That's actually why I wrote it - as a "template" for how to do similar user defined types.


> The rest of the code is tl;dr, but I think it does showcase quite well
> the power of user-defined types in D. Now we just have to iron out
> implicit conversion from literals (and I mean that in general, not just
> for this particular example), and it will just be great.

Yup. Once all that works, I'd like to incorporate halffloat into Phobos.

December 15, 2012
On Fri, Dec 14, 2012 at 12:39:04AM -0800, Walter Bright wrote:
> On 12/13/2012 10:19 PM, H. S. Teoh wrote:
> >This is cool, I've never thought of using alias this on a @property function. I'll have to start using that in my code. :-)
> 
> That's actually why I wrote it - as a "template" for how to do similar user defined types.
[...]

I'm so impressed by this trick that I decided to do a writeup of it on the wiki:

	http://wiki.dlang.org/Implicit_conversions_in_user_types


T

-- 
If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
December 15, 2012
On 12/14/2012 4:02 PM, H. S. Teoh wrote:
> I'm so impressed by this trick that I decided to do a writeup of it on
> the wiki:
>
> 	http://wiki.dlang.org/Implicit_conversions_in_user_types

I'd prefer to call it a "technique" rather than a "trick", because this is what alias this was designed for!

December 15, 2012
On Saturday, 15 December 2012 at 02:23:18 UTC, Walter Bright wrote:
> I'd prefer to call it a "technique" rather than a "trick", because this is what alias this was designed for!


BTW is there any idea for the timetable of multiple alas this like described in Andrei's book?
December 15, 2012
On 12/14/2012 6:27 PM, Adam D. Ruppe wrote:
> BTW is there any idea for the timetable of multiple alas this like described in
> Andrei's book?

Not at the moment.
December 16, 2012
On Tuesday, 11 December 2012 at 12:53:38 UTC, monarch_dodra wrote:
> integer operations are always promoted to at least int. That's standard fare since C. I think it is a performance thing: Unpack into ints, oeprate, repack into ints.

 If memory serves me right, promotion to int is based more on CPU efficiency and code size than anything else (and yes packing and unpacking takes some 6 extra steps). Also with libraries possibly written by different compilers may have different results if they don't all agree on a common (default) type to return.

 Also most x86 operations for ints can be written as 1 byte opcodes, although for modern CPUs that kind of code generation may not be as efficient as it used to be.

http://www.codeproject.com/Articles/6154/Writing-Efficient-C-and-C-Code-Optimization
1 2 3 4
Next ›   Last »