www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - floating point verification using is?

reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
If I have 2 identical floating point values, how do I ensure they are  
binary equivalents of eachother?  I'm trying to write some unittest/assert  
code, and it's not exactly trivial.

I thought 'a is b' would work, but it just morphs into a == b, which isn't  
helpful.  Why doesn't 'is' just do a bit compare for floating points?

-Steve
Dec 18 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:

 If I have 2 identical floating point values, how do I ensure they are  
 binary equivalents of eachother?
Try this inside std.math of Phobos2: bool isIdentical(real x, real y);
 I thought 'a is b' would work, but it just morphs into a == b, which isn't  
 helpful.  Why doesn't 'is' just do a bit compare for floating points?
"is" is used to compare references. Bye, bearophile
Dec 18 2009
next sibling parent grauzone <none example.net> writes:
bearophile wrote:
 Steven Schveighoffer:
 
 If I have 2 identical floating point values, how do I ensure they are  
 binary equivalents of eachother?
Try this inside std.math of Phobos2: bool isIdentical(real x, real y);
 I thought 'a is b' would work, but it just morphs into a == b, which isn't  
 helpful.  Why doesn't 'is' just do a bit compare for floating points?
"is" is used to compare references.
No. If you use it with structs, the type's opEquals is not called. "is" is used for a lot of things. I agree that is should do a bitwise comparison for floating points. That would be a nice fix for D2.
 Bye,
 bearophile
Dec 18 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 18 Dec 2009 12:34:09 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:

 If I have 2 identical floating point values, how do I ensure they are
 binary equivalents of eachother?
Try this inside std.math of Phobos2: bool isIdentical(real x, real y);
Thanks, that seems to be what I want.
 I thought 'a is b' would work, but it just morphs into a == b, which  
 isn't
 helpful.  Why doesn't 'is' just do a bit compare for floating points?
"is" is used to compare references.
to me, is means "ignore semantic meaning, do a bitwise compare" regardless of reference status. For example comparing 2 array structs using == will check that all the data is the same, but using "is" makes it compare bitwise the structs directly. I see no difference with floating points, except you can't get at the bits easily. The existence of isIdentical could be completely replaced by one or two instructions generated by the compiler when it sees "float is float". -Steve
Dec 18 2009
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 18 Dec 2009 14:16:17 -0500, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 On Fri, 18 Dec 2009 12:34:09 -0500, bearophile  
 <bearophileHUGS lycos.com> wrote:

 Steven Schveighoffer:

 If I have 2 identical floating point values, how do I ensure they are
 binary equivalents of eachother?
Try this inside std.math of Phobos2: bool isIdentical(real x, real y);
Thanks, that seems to be what I want.
Except it doesn't work with literals properly: float x; assert(isIdentical(x, float.init)); // fails This is what I'm trying to test. It has to do with the fact that float.init is a literal, and I think it's automatically converted to real.init. This code works: float x; float y; assert(isIdentical(x, y)); I'm going to file a bug on this, float is float should just work! -Steve
Dec 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 float x;
 
 assert(isIdentical(x, float.init)); // fails
 
 This is what I'm trying to test.  It has to do with the fact that  
 float.init is a literal, and I think it's automatically converted to  
 real.init.
Try this: http://www.digitalmars.com/d/2.0/phobos/std_math.html#isNaN Bye, bearophile
Dec 18 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 18 Dec 2009 14:47:13 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:
 float x;

 assert(isIdentical(x, float.init)); // fails

 This is what I'm trying to test.  It has to do with the fact that
 float.init is a literal, and I think it's automatically converted to
 real.init.
Try this: http://www.digitalmars.com/d/2.0/phobos/std_math.html#isNaN
That's great, but I'm trying to verify that my array building code correctly appends T.init. isNaN returns true no matter what the bit representation of nan is. I want to *specifically* compare bit representations of floating point numbers to ensure the code I'm writing is doing what I think it's doing. It shouldn't be this complicated to do that. -Steve
Dec 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 That's great, but I'm trying to verify that my array building code  
 correctly appends T.init.  isNaN returns true no matter what the bit  
 representation of nan is.  I want to *specifically* compare bit  
 representations of floating point numbers to ensure the code I'm writing  
 is doing what I think it's doing.
I see :-) Let's try again (but next time please explain the full problem in your first post, and not a slice of it): import std.c.stdio: printf; bool isNanInit(T)(T f) if (is(T == double) || is(T == float)) { union FPInt { T f; static if (is(T == float)) uint u; static if (is(T == double)) ulong u; } static FPInt fnan, fx; fx.f = f; return fnan.u == fx.u; } void main() { printf("%d\n", isNanInit(2.5)); float x; printf("%d\n", isNanInit(x)); x = 2.5; printf("%d\n", isNanInit(x)); } Bye, bearophile
Dec 18 2009
parent bearophile <bearophileHUGS lycos.com> writes:
This may be better, I have not taken a look at the resulting asm yet:

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

bool isInitNan(T)(T f) if (isFloatingPoint!T) {
    union FPInt {
        T f;
        ubyte[T.sizeof] a;
    }
    static FPInt fnan, fx;
    fx.f = f;
    return fnan.a == fx.a;
}

void main() {
    printf("%d\n", isInitNan(2.5));
    float x;
    printf("%d\n", isInitNan(x));
    x = 2.5;
    printf("%d\n", isInitNan(x));
}
Dec 18 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 to me, is means "ignore semantic meaning, do a bitwise compare" regardless  
 of reference status.
 
 For example comparing 2 array structs using == will check that all the  
 data is the same, but using "is" makes it compare bitwise the structs  
 directly.
 
 I see no difference with floating points, except you can't get at the bits  
 easily.  The existence of isIdentical could be completely replaced by one  
 or two instructions generated by the compiler when it sees "float is  
 float".
You can try expressing this idea in the main D group. Bye, bearophile
Dec 18 2009
prev sibling next sibling parent reply div0 <div0 users.sourceforge.net> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Steven Schveighoffer wrote:
 If I have 2 identical floating point values, how do I ensure they are
 binary equivalents of eachother?  I'm trying to write some
 unittest/assert code, and it's not exactly trivial.
 
 I thought 'a is b' would work, but it just morphs into a == b, which
 isn't helpful.  Why doesn't 'is' just do a bit compare for floating points?
 
 -Steve
Just out of curiosity; how many cases are there where a == b and they have different bit patterns? I can only think of +-0. Otherwise you can only get that with denormalised floats; which you won't get on an x86 processor. - -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iD8DBQFLK86uT9LetA9XoXwRAvtsAKDDoX6EzXj+wOIgTVHDzkRF0vXGsgCdH6tX QLvFQxYyjIwk7Qxi13VyrCI= =M18X -----END PGP SIGNATURE-----
Dec 18 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 18 Dec 2009 13:49:18 -0500, div0 <div0 users.sourceforge.net>  
wrote:

 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1

 Steven Schveighoffer wrote:
 If I have 2 identical floating point values, how do I ensure they are
 binary equivalents of eachother?  I'm trying to write some
 unittest/assert code, and it's not exactly trivial.

 I thought 'a is b' would work, but it just morphs into a == b, which
 isn't helpful.  Why doesn't 'is' just do a bit compare for floating  
 points?

 -Steve
Just out of curiosity; how many cases are there where a == b and they have different bit patterns? I can only think of +-0. Otherwise you can only get that with denormalised floats; which you won't get on an x86 processor.
nan is the specific one I was trying to test for. From my very hazy memory, I think there can even be multiple binary representations of nan, but I'm not sure. -Steve
Dec 18 2009
parent BCS <none anon.com> writes:
Hello Steven,

 From my very hazy memory, I think there can even be multiple binary
 representations of nan, but I'm not sure.
 
 -Steve
 
yes, IIRC NaNs can have a few bits of payload as well as there being signaling and non-signaling NaNs.
Dec 18 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Dec 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
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));
}
Dec 18 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Dec 18 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Dec 18 2009
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
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.
Dec 27 2009
parent reply Don <nospam nospam.com> writes:
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.
Dec 28 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Dec 28 2009
parent reply Don <nospam nospam.com> writes:
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.
Dec 28 2009
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Dec 28 2009
parent reply Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 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?
The thing is, if you've written something to a memory location, it's not uninitialized any more.
 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'.
I think we could get the behaviour you want by changing the definition of T.init. I think it should ALWAYS be a bug to read an uninitialized variable; but it should be OK to have: T x = T.init; if (x == T.init) ... Otherwise, there's no point in making T.init available.
Dec 29 2009
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Don wrote:
<snip>
 I think we could get the behaviour you want by changing the definition 
 of T.init. I think it should ALWAYS be a bug to read an uninitialized 
 variable;
<snip> All variables in D are initialized, unless overridden with a void initializer. But what would "ALWAYS be a bug" mean? (a) Trigger a compile-time error? While some C(++) compilers will warn you if they catch you trying to read a variable before it's been set, they cannot be perfect at doing so. That's probably one reason that D takes the simpler route. (b) Trigger a run-time error? This would be a performance hit, as you'd need - extra working memory, as a bit to keep track of whether each variable is set - extra code to set and check this bit when a variable is used - extra CPU cycles to execute this extra code I'd be inclined to save this kind of behaviour for scripting languages. (c) Trigger undefined behaviour? I can't see any real way of making it undefined short of going back to the C way. One of D's reasons for initializing all variables is so that bugs caused by reading variables before they're otherwise set are easier to diagnose, since the behaviour doesn't change with every execution. Another reason I can see is to avoid memory trample, possibly also GC horrors, caused by leaving pointers accidentally pointing somewhere and then trying to use them. While you could get around this by defining that reference types are initialized to null and value types are not initialized, it's simpler to define that all variables are initialized. A further complication is that, in a struct or class, some members may have default initializers set and others not. Initializing a variable of struct or class type is much simpler if the fact of being initialized applies to the whole struct rather than some of its members. (d) Be perfectly legal and defined, yet deemed immoral? Stewart.
Dec 30 2009
parent Don <nospam nospam.com> writes:
Stewart Gordon wrote:
 Don wrote:
 <snip>
 I think we could get the behaviour you want by changing the definition 
 of T.init. I think it should ALWAYS be a bug to read an uninitialized 
 variable;
<snip> All variables in D are initialized, unless overridden with a void initializer. But what would "ALWAYS be a bug" mean? (a) Trigger a compile-time error? While some C(++) compilers will warn you if they catch you trying to read a variable before it's been set, they cannot be perfect at doing so. That's probably one reason that D takes the simpler route.
Yes, but it can be a QOI issue. That is, it's always a bug, the compiler will do the best it can to detect it at compile time, but it isn't always possible. Proof: bool arbitrarily_difficult() {} int main() { int x; if (arbitrarily_difficult()) x=7; return x; } So it's clearly an NP-hard problem. So we have run-time initialisation to catch the cases that are too hard for the compiler.
 (b) Trigger a run-time error?
 
 This would be a performance hit, as you'd need
 - extra working memory, as a bit to keep track of whether each variable 
 is set
 - extra code to set and check this bit when a variable is used
 - extra CPU cycles to execute this extra code
 
 I'd be inclined to save this kind of behaviour for scripting languages.
 
 (c) Trigger undefined behaviour?
 
 I can't see any real way of making it undefined short of going back to 
 the C way.
 
 One of D's reasons for initializing all variables is so that bugs caused 
 by reading variables before they're otherwise set are easier to 
 diagnose, since the behaviour doesn't change with every execution.
 
 Another reason I can see is to avoid memory trample, possibly also GC 
 horrors, caused by leaving pointers accidentally pointing somewhere and 
 then trying to use them.  While you could get around this by defining 
 that reference types are initialized to null and value types are not 
 initialized, it's simpler to define that all variables are initialized.
 
 A further complication is that, in a struct or class, some members may 
 have default initializers set and others not.  Initializing a variable 
 of struct or class type is much simpler if the fact of being initialized 
 applies to the whole struct rather than some of its members.
 
 (d) Be perfectly legal and defined, yet deemed immoral?
The question is, are they initialized solely in order to help detect bugs, or are they initialized so that you can use them?
Dec 30 2009
prev sibling parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
Don wrote:
 Steven Schveighoffer wrote:
 That value should always be consistent.  There has to be a way to=20
 verify that the value was set properly.  Otherwise, how do you verify =
 things
 like communication protocols or expectations for runtime functions?
=20 I think that sending an uninitialized variable down a communication=20 channel is always a bug. =20
Yes, it's a bug in the *sender*. However, the *receiver* should be=20 able to handle the case gracefully (and stopping because of a=20 signalling NaN is not "gracefully" :) Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Dec 29 2009