Jump to page: 1 2
Thread overview
Re: floating point verification using is?
Dec 18, 2009
bearophile
Dec 18, 2009
bearophile
Dec 19, 2009
bearophile
Dec 27, 2009
Stewart Gordon
Dec 28, 2009
Don
Dec 29, 2009
Don
Dec 30, 2009
Don
To init or not to init? (was: floating point verification using is?)
Dec 30, 2009
Stewart Gordon
Re: To init or not to init?
Dec 30, 2009
Don
Dec 29, 2009
Jérôme M. Berger
December 18, 2009
Third try, this probably compiles better:

bool isInitNan(T)(T f) if (isFloatingPoint!T) {
    union FPInt {
        T f;
        static if (is(T == float))
            uint data;
        static if (is(T == double))
            ulong data;
        static if (is(T == real))
            ubyte[real.sizeof] data;
    }
    static FPInt fnan, fx;
    fx.f = f;
    return fnan.data == fx.data;
}

Bye,
bearophile
December 18, 2009
4th try, getting there, this helps if in your compiler real == double:

import std.traits: isFloatingPoint;
import std.c.stdio: printf;

bool isInitNaN(T)(T x) if (isFloatingPoint!T) {
    union FPData {
        T f;
        static if (T.sizeof == uint.sizeof)
            uint data;
        else static if (T.sizeof == ulong.sizeof)
            ulong data;
        else
            ubyte[T.sizeof] data;
    }
    static FPData fnan, fpd;
    fpd.f = x;
    return fnan.data == fpd.data;
}

void main() {
    printf("%d\n", isInitNaN(2.5f));
    printf("%d\n", isInitNaN(2.5));
    printf("%d\n", isInitNaN(2.5L));

    float xf;
    printf("%d\n", isInitNaN(xf));
    xf = 2.5;
    printf("%d\n", isInitNaN(xf));

    double xd;
    printf("%d\n", isInitNaN(xd));
    xd = 2.5;
    printf("%d\n", isInitNaN(xd));

    float xr;
    printf("%d\n", isInitNaN(xr));
    xr = 2.5;
    printf("%d\n", isInitNaN(xr));
}
December 19, 2009
Unfortunately the asm produced by ldc shows the call to the array equality function still. Fifth try:


bool isInitNaN(T)(T x) if (isFloatingPoint!T) {
    union FPData {
        T f;

        static if (T.sizeof == uint.sizeof) {
            uint data;
        } else static if (T.sizeof == ulong.sizeof) {
            ulong data;
        } else static if (T.sizeof == 10) {
            ulong data1;
            ushort data2;
        } else static if (T.sizeof == 12) {
            ulong data1;
            uint data2;
        } else static if (T.sizeof == 16) {
            ulong data1;
            ulong data2;
        } else {
            ubyte[T.sizeof] data;
        }
    }

    static FPData fnan, fpd;
    fpd.f = x;

    static if (T.sizeof == 10 || T.sizeof == 12 || T.sizeof == 16)
        return fnan.data1 == fpd.data1 && fnan.data2 == fpd.data2;
    else
        return fnan.data == fpd.data;
}

Bye,
bearophile
December 19, 2009
On Fri, 18 Dec 2009 21:47:22 -0500, bearophile <bearophileHUGS@lycos.com> wrote:

> Unfortunately the asm produced by ldc shows the call to the array equality function still. Fifth try:
>
>
> bool isInitNaN(T)(T x) if (isFloatingPoint!T) {
>     union FPData {
>         T f;
>
>         static if (T.sizeof == uint.sizeof) {
>             uint data;
>         } else static if (T.sizeof == ulong.sizeof) {
>             ulong data;
>         } else static if (T.sizeof == 10) {
>             ulong data1;
>             ushort data2;
>         } else static if (T.sizeof == 12) {
>             ulong data1;
>             uint data2;
>         } else static if (T.sizeof == 16) {
>             ulong data1;
>             ulong data2;
>         } else {
>             ubyte[T.sizeof] data;
>         }
>     }
>
>     static FPData fnan, fpd;
>     fpd.f = x;
>
>     static if (T.sizeof == 10 || T.sizeof == 12 || T.sizeof == 16)
>         return fnan.data1 == fpd.data1 && fnan.data2 == fpd.data2;
>     else
>         return fnan.data == fpd.data;
> }
>
> Bye,
> bearophile

Are you still working on this?  :)  I think this proves my point.  The compiler does not provide an easy way to compare floats bitwise, so this means convoluted hard-to-write code.  When we have two operators that do equality -- and one of those means bitwise compare in all other contexts -- I think this is a no-brainer.

-Steve
December 27, 2009
Steven Schveighoffer wrote:
<snip>
> Are you still working on this?  :)  I think this proves my point.  The compiler does not provide an easy way to compare floats bitwise, so this means convoluted hard-to-write code.  When we have two operators that do equality -- and one of those means bitwise compare in all other contexts -- I think this is a no-brainer.

I entirely agree.

I've identified these cases in which float equality disagrees with bitwise equality:
- one is +0, one is -0
- both are NaNs, identically signed
- both are infinity, identically signed

In each case, is just does the same as ==.

Indeed, isIdentical is an ugly workaround for this, apparently created instead of sanitising the definition of is to avoid the minuscule amount of code breakage that the latter would effect.

Stewart.
December 28, 2009
Stewart Gordon wrote:
> Steven Schveighoffer wrote:
> <snip>
>> Are you still working on this?  :)  I think this proves my point.  The compiler does not provide an easy way to compare floats bitwise, so this means convoluted hard-to-write code.  When we have two operators that do equality -- and one of those means bitwise compare in all other contexts -- I think this is a no-brainer.
> 
> I entirely agree.
> 
> I've identified these cases in which float equality disagrees with bitwise equality:
> - one is +0, one is -0
> - both are NaNs, identically signed
> - both are infinity, identically signed
> 
> In each case, is just does the same as ==.
> 
> Indeed, isIdentical is an ugly workaround for this, apparently created instead of sanitising the definition of is to avoid the minuscule amount of code breakage that the latter would effect.

No, it was because I have write access to Phobos, but not to the compiler...

But, this isn't as simple as people are assuming. There's a problem in the original request. We need to be clear about what T.init is. Is T.init
(a) the bit pattern which all variables of type T get default initialized to? Or is it
(b) an arbitrary valid value for type T?
In the case of floats, they are NOT "default initialized" -- they are marked as "not initialized". So there's a big question about whether semantic (a) T.init actually makes sense.

Consider this code:

T x;
if (x==T.init) { writefln("x is uninitialized"); }

Is this code valid? You are reading the value of an uninitialized variable.
December 28, 2009
On Mon, 28 Dec 2009 06:51:30 -0500, Don <nospam@nospam.com> wrote:

> Stewart Gordon wrote:
>> Steven Schveighoffer wrote:
>> <snip>
>>> Are you still working on this?  :)  I think this proves my point.  The compiler does not provide an easy way to compare floats bitwise, so this means convoluted hard-to-write code.  When we have two operators that do equality -- and one of those means bitwise compare in all other contexts -- I think this is a no-brainer.
>>  I entirely agree.
>>  I've identified these cases in which float equality disagrees with bitwise equality:
>> - one is +0, one is -0
>> - both are NaNs, identically signed
>> - both are infinity, identically signed
>>  In each case, is just does the same as ==.
>>  Indeed, isIdentical is an ugly workaround for this, apparently created instead of sanitising the definition of is to avoid the minuscule amount of code breakage that the latter would effect.
>
> No, it was because I have write access to Phobos, but not to the compiler...
>
> But, this isn't as simple as people are assuming. There's a problem in the original request. We need to be clear about what T.init is. Is T.init
> (a) the bit pattern which all variables of type T get default initialized to? Or is it
> (b) an arbitrary valid value for type T?
> In the case of floats, they are NOT "default initialized" -- they are marked as "not initialized". So there's a big question about whether semantic (a) T.init actually makes sense.

The answer is a.  Whether you like it or not, the runtime must initialize it to something, even if the value means "uninitialized".  That value should always be consistent.  There has to be a way to verify that the value was set properly.  Otherwise, how do you verify things like communication protocols or expectations for runtime functions?

It has to be a bit pattern, because all other T.init values, including those of aggregates which might contain floating points, are bit patterns.

> Consider this code:
>
> T x;
> if (x==T.init) { writefln("x is uninitialized"); }
>
> Is this code valid? You are reading the value of an uninitialized variable.

Correct that to:

T x;
if(x is T.init) {writefln("x is uninitialized"); }

Then I think it is valid.  I don't care if == is hijacked away from bitwise comparison, in fact, it is necessary in many cases.  But there should be a way to do bitwise comparison for verification, and 'is' fits the bill perfectly.

-Steve
December 29, 2009
Steven Schveighoffer wrote:
> On Mon, 28 Dec 2009 06:51:30 -0500, Don <nospam@nospam.com> wrote:
> 
>> Stewart Gordon wrote:
>>> Steven Schveighoffer wrote:
>>> <snip>
>>>> Are you still working on this?  :)  I think this proves my point.  The compiler does not provide an easy way to compare floats bitwise, so this means convoluted hard-to-write code.  When we have two operators that do equality -- and one of those means bitwise compare in all other contexts -- I think this is a no-brainer.
>>>  I entirely agree.
>>>  I've identified these cases in which float equality disagrees with bitwise equality:
>>> - one is +0, one is -0
>>> - both are NaNs, identically signed
>>> - both are infinity, identically signed
>>>  In each case, is just does the same as ==.
>>>  Indeed, isIdentical is an ugly workaround for this, apparently created instead of sanitising the definition of is to avoid the minuscule amount of code breakage that the latter would effect.
>>
>> No, it was because I have write access to Phobos, but not to the compiler...
>>
>> But, this isn't as simple as people are assuming. There's a problem in the original request. We need to be clear about what T.init is. Is T.init
>> (a) the bit pattern which all variables of type T get default initialized to? Or is it
>> (b) an arbitrary valid value for type T?
>> In the case of floats, they are NOT "default initialized" -- they are marked as "not initialized". So there's a big question about whether semantic (a) T.init actually makes sense.
> 
> The answer is a.  Whether you like it or not, the runtime must initialize it to something, even if the value means "uninitialized".  

The key question is: is it valid to read an uninitialized variable? I would hope the compiler would flag that as an error. The use of signalling NaNs was based on the assumption that you should *never* be reading uninitialized variables.

A consequence is that you should also NEVER read float.init!

> That value should always be consistent.  There has to be a way to verify that the value was set properly.  Otherwise, how do you verify things
> like communication protocols or expectations for runtime functions?

I think that sending an uninitialized variable down a communication channel is always a bug.

> It has to be a bit pattern, because all other T.init values, including those of aggregates which might contain floating points, are bit patterns.
> 
>> Consider this code:
>>
>> T x;
>> if (x==T.init) { writefln("x is uninitialized"); }
>>
>> Is this code valid? You are reading the value of an uninitialized variable.
> 
> Correct that to:
> 
> T x;
> if(x is T.init) {writefln("x is uninitialized"); }
> 
> Then I think it is valid.  I don't care if == is hijacked away from bitwise comparison, in fact, it is necessary in many cases.  But there should be a way to do bitwise comparison for verification, and 'is' fits the bill perfectly.

That does preclude a compiler from ever giving you errors/warnings about using uninitialized variables. I think it's wrong.
December 29, 2009
On Mon, 28 Dec 2009 21:52:24 -0500, Don <nospam@nospam.com> wrote:

> Steven Schveighoffer wrote:
>> Whether you like it or not, the runtime must initialize it to something, even if the value means "uninitialized".
>
> The key question is: is it valid to read an uninitialized variable? I would hope the compiler would flag that as an error. The use of signalling NaNs was based on the assumption that you should *never* be reading uninitialized variables.
>
> A consequence is that you should also NEVER read float.init!

I am not even close to a floating point expert.  In fact, I try to avoid them like the plague.  All I want to do is verify that the bit pattern being written to a memory location is the bit pattern I expect to be written.  How do I do that without comparing what I expect to what I wrote?

>
>> That value should always be consistent.  There has to be a way to verify that the value was set properly.  Otherwise, how do you verify things
>> like communication protocols or expectations for runtime functions?
>
> I think that sending an uninitialized variable down a communication channel is always a bug.

Just wait, someone will define it :)  If I want to verify that it's any value *but* T.init, how do I do that?  And I don't think using <>=!@%$ to do it is worth the trouble.

>> It has to be a bit pattern, because all other T.init values, including those of aggregates which might contain floating points, are bit patterns.
>>
>>> Consider this code:
>>>
>>> T x;
>>> if (x==T.init) { writefln("x is uninitialized"); }
>>>
>>> Is this code valid? You are reading the value of an uninitialized variable.
>>  Correct that to:
>>  T x;
>> if(x is T.init) {writefln("x is uninitialized"); }
>>  Then I think it is valid.  I don't care if == is hijacked away from bitwise comparison, in fact, it is necessary in many cases.  But there should be a way to do bitwise comparison for verification, and 'is' fits the bill perfectly.
>
> That does preclude a compiler from ever giving you errors/warnings about using uninitialized variables. I think it's wrong.

I'm not using it in every-day code (although I don't think it's wrong to do so), I'm using it for asserts and unit tests.  I'm asserting that what's happening is what I expect is happening.  To make this unreasonably difficult makes no sense to me.  To say "is works on everything but floating point initializers" sounds ridiculously inconsistent.  Maybe preventing it is the Right Thing to do, but it is possible to do, so what harm can it cause?  It wouldn't even cause an exception.

Note that I don't think == should change behavior, just 'is'.

-Steve
December 29, 2009
Don wrote:
> Steven Schveighoffer wrote:
>> That value should always be consistent.  There has to be a way to
>> verify that the value was set properly.  Otherwise, how do you verify
>> things
>> like communication protocols or expectations for runtime functions?
> 
> I think that sending an uninitialized variable down a communication channel is always a bug.
> 
	Yes, it's a bug in the *sender*. However, the *receiver* should be
able to handle the case gracefully (and stopping because of a
signalling NaN is not "gracefully" :)

		Jerome
-- 
mailto:jeberger@free.fr
http://jeberger.free.fr
Jabber: jeberger@jabber.fr



« First   ‹ Prev
1 2