Comparing floating point: == vs is

Jonathan M Davis jmdavisProg at gmx.com
Sat Jul 16 14:55:11 PDT 2011


On Saturday 16 July 2011 21:19:27 Daniel Murphy wrote:
> "Jonathan M Davis" <jmdavisProg at gmx.com> wrote in message
> news:mailman.1690.1310809801.14074.digitalmars-d-learn at puremagic.com...
> 
> > I tried both casting to an integer of the appropriate size and swapping
> > that
> > and doing this:
> > 
> > private T swapEndianImpl(T)(T val)
> > 
> >    if(isFloatingPoint!T)
> > 
> > {
> > 
> >    import std.algorithm;
> >    
> >    union Union
> >    {
> >    
> >        Unqual!T        _floating;
> >        ubyte[T.sizeof] _array;
> >    
> >    }
> >    
> >    Union u;
> >    u._floating = val;
> >    std.algorithm.reverse(u._array[]);
> >    
> >    return u._floating;
> > 
> > }
> > 
> > I was testing the function by reversing the values twice, and casting
> > seemed
> > to fry NaNs, whereas the union/array trick seems to usually result in
> > the
> > correct values when I print them (at least for the ones I've looked at)
> > and is
> > was returning true for most of them, but == has been failing.
> > 
> > I don't know anything about padding bytes in floating point though, so
> > maybe
> > that's part of the problem.
> > http://stackoverflow.com/questions/2782725/converting-float-values-from-
> > big- endian-to-little-endian/2782742#2782742 seemed to indicate that I
> > could just
> > swap the bytes using the union/array trick, but I'm not understanding
> > something here and/or something is off with either my implementation or
> > the
> > compiler.
> > 
> > - Jonathan M Davis
> 
> The padding issue only applies to reals.  On x86 they're 10 bytes with 0
> bytes padding (windows) or 2 bytes padding (linux) or 6 bytes padding (osx).
> 
> I don't know why it would be failing for floats or doubles though.  Do you
> have some tests that fail?

This is also being discussed on the main list in the "Byte Order Swapping" 
thread, so you may want to discuss it there, but this is what I have at the 
moment:

import core.bitop;

/++
    Swaps the endianness of the given value. Any integral value,
    character, or floating point value is accepted.
  +/
T swapEndian(T)(T val)
    if(isNumeric!T || isSomeChar!T)
{
    static if(val.sizeof == 1)
        return val;
    else static if(isUnsigned!T || isFloatingPoint!T)
        return swapEndianImpl(val);
    else static if(isIntegral!T)
        return swapEndianImpl(cast(Unsigned!T) val);
    else static if(is(Unqual!T == wchar))
        return cast(T)swapEndian(cast(ushort)val);
    else static if(is(Unqual!T == dchar))
        return cast(T)swapEndian(cast(uint)val);
    else
        static assert(0, T.stringof ~ " unsupported by swapEndian.");
}

private T swapEndianImpl(T)(T val)
    if(is(Unqual!T == ushort))
{
    return ((val & 0xff00U) >> 8) |
           ((val & 0x00ffU) << 8);
}

private T swapEndianImpl(T)(T val)
    if(is(Unqual!T == uint))
{
    return bswap(val);
}

private T swapEndianImpl(T)(T val)
    if(is(Unqual!T == ulong))
{
    return ((val & 0xff00000000000000UL) >> 56) |
           ((val & 0x00ff000000000000UL) >> 40) |
           ((val & 0x0000ff0000000000UL) >> 24) |
           ((val & 0x000000ff00000000UL) >> 8) |
           ((val & 0x00000000ff000000UL) << 8) |
           ((val & 0x0000000000ff0000UL) << 24) |
           ((val & 0x000000000000ff00UL) << 40) |
           ((val & 0x00000000000000ffUL) << 56);
}

private T swapEndianImpl(T)(T val)
    if(isFloatingPoint!T)
{
    import std.algorithm;

    union Union
    {
        Unqual!T        _floating;
        ubyte[T.sizeof] _array;
    }

    Union u;
    u._floating = val;
    std.algorithm.reverse(u._array[]);

    return u._floating;
}

unittest
{
    import std.stdio;
    import std.typetuple;

    foreach(T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong,
                          char, wchar, dchar, float, double, real))
    {
        scope(failure) writefln("Failed type: %s", T.stringof);
        T val;
        const T cval;
        immutable T ival;

        assert(swapEndian(swapEndian(val)) == val);
        assert(swapEndian(swapEndian(cval)) == cval);
        assert(swapEndian(swapEndian(ival)) == ival);
        assert(swapEndian(swapEndian(T.min)) == T.min);
        assert(swapEndian(swapEndian(T.max)) == T.max);
        static if(isSigned!T) assert(swapEndian(swapEndian(cast(T)0)) == 0);
    }
}

I suspect that the problem is a combination of issues with NaN and real. If I 
change the first three tests (which test init which is NaN) to is, then they 
pass, but the test for real.max fails regardless of whether is or == is used. 
The other tests seem to pass regardless of whether is or == is used. So, I may 
need to do something special for NaN, and I may have to do something to deal 
with the padding in real. I don't know.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list