View mode: basic / threaded / horizontal-split · Log in · Help
April 14, 2012
floats default to NaN... why?
From the FaQ:

> NaNs have the interesting property in that whenever a NaN is 
> used as an operand in a computation, the result is a NaN. 
> Therefore, NaNs will propagate and appear in the output 
> whenever a computation made use of one. This implies that a NaN 
> appearing in the output is an unambiguous indication of the use 
> of an uninitialized variable.

> If 0.0 was used as the default initializer for floating point 
> values, its effect could easily be unnoticed in the output, and 
> so if the default initializer was unintended, the bug may go 
> unrecognized.


So basically, it's for debugging? Is that it's only reason? If so 
I'm at loss as to why default is NaN. The priority should always 
be in the ease-of-use IMO. Especially when it breaks a "standard":

    struct Foo {
      int x, y;    // ready for use.
      float z, w;  // messes things up.
      float r = 0; // almost always...
    }

I'm putting this in .Learn because I'm not really suggesting a 
change as much as trying to learn the reasoning behind it. The 
break in consistency doesn't outweigh any cost of "debugging" 
benefit I can see. I'm not convinced there is any. Having core 
numerical types always and unanimously default to zero is 
understandable and consistent (and what I'm use too with C#). The 
above could be written as:

    struct Foo {
      float z = float.nan, ...
    }

if you wanted to guarantee the values are set uniquely at 
construction. Which seems like a job better suited for unittests 
to me anyways.

musing...
April 14, 2012
Re: floats default to NaN... why?
On Saturday, April 14, 2012 06:00:35 F i L wrote:
>  From the FaQ:
> > NaNs have the interesting property in that whenever a NaN is
> > used as an operand in a computation, the result is a NaN.
> > Therefore, NaNs will propagate and appear in the output
> > whenever a computation made use of one. This implies that a NaN
> > appearing in the output is an unambiguous indication of the use
> > of an uninitialized variable.
> > 
> > If 0.0 was used as the default initializer for floating point
> > values, its effect could easily be unnoticed in the output, and
> > so if the default initializer was unintended, the bug may go
> > unrecognized.
> 
> So basically, it's for debugging? Is that it's only reason? If so
> I'm at loss as to why default is NaN. The priority should always
> be in the ease-of-use IMO. Especially when it breaks a "standard":
> 
>      struct Foo {
>        int x, y;    // ready for use.
>        float z, w;  // messes things up.
>        float r = 0; // almost always...
>      }
> 
> I'm putting this in .Learn because I'm not really suggesting a
> change as much as trying to learn the reasoning behind it. The
> break in consistency doesn't outweigh any cost of "debugging"
> benefit I can see. I'm not convinced there is any. Having core
> numerical types always and unanimously default to zero is
> understandable and consistent (and what I'm use too with C#). The
> above could be written as:
> 
>      struct Foo {
>        float z = float.nan, ...
>      }
> 
> if you wanted to guarantee the values are set uniquely at
> construction. Which seems like a job better suited for unittests
> to me anyways.
> 
> musing...

Types default to the closest thing that they have to an invalid value so that 
code blows up as soon as possible if you fail to initialize a variable to a 
proper value and so that it fails deterministically (unlike when variables 
aren't initialized and therefore have garbage values).

NaN is the invalid value for floating point types and works fantastically at 
indicating that you screwed up and failed to initialize or assign your 
variable a proper value. null for pointers and references works similarily 
well.

If anything, the integral types and bool fail, because they don't _have_ 
invalid values. The closest that they have is 0 and false respectively, so 
that's what they get. It's the integral types that are inconsistent, not the 
floating point types.

It was never really intended that variables would be default initialized with 
values that you would use. You're supposed to initialize them or assign them 
to appropriate values before using them. Now, since the default values are 
well-known and well-defined, you can rely on them if you actually _want_ those 
values, but the whole purpose of default initialization is to make code fail 
deterministically when variables aren't properly initialized - and to fail as 
quickly as possible.

- Jonathan M Davis
April 14, 2012
Re: floats default to NaN... why?
Am 14.04.2012 06:00, schrieb F i L:
>       struct Foo {
>         int x, y;    // ready for use.
>         float z, w;  // messes things up.
>         float r = 0; // almost always...
>       }

how often in your code is 0 or 0.0 the real starting point?
i can't think of any situation except counters or something
where 0 is a proper start - and float 0.0 is in very very few cases a 
normal start - so whats your point?
April 14, 2012
Re: floats default to NaN... why?
So it's what I thought, the only reason is based on a faulty 
premise, IMO.

Jonathan M Davis wrote:
> Types default to the closest thing that they have to an invalid 
> value so that
> code blows up as soon as possible if you fail to initialize a 
> variable to a
> proper value and so that it fails deterministically

This seems like exactly opposite behavior I'd expect from the 
compiler. Modern convenience means more bang per character, and 
initializing values to garbage is the corner case, not the usual 
case.

> (unlike when variables
> aren't initialized and therefore have garbage values).

This is the faulty premise I see. Garbage values are a C/C++ 
thing. They must be forced in D, eg, float x = void.

I would argue that because values *should* have implicit, 
non-garbage, default values that those default values should be 
the most commonly used/expected "lowest" value. Especially since 
ints _must_ be 0 (though I hear this is changing in Arm64).


> NaN is the invalid value for floating point types and works 
> fantastically at
> indicating that you screwed up and failed to initialize or 
> assign your
> variable a proper value.

> null for pointers and references works similarily
> well.

Not exactly. NaNs don't cause Segfaults or Undefined behavior, 
they just make the math go haywire. It's like it was designed to 
be inconvenient. The argument looks like this to me:

"We default values so there's no garbage-value bugs.. but the 
default is something that will cause a bug.. because values 
should be explicitly defaulted so they're not unexpected values 
(garbage).. even though we could default them to an expected 
value since we're doing it to begin with"

It sounds like circular reasoning.


> It was never really intended that variables would be default 
> initialized with
> values that you would use.

why exactly? again, this is a faulty premise IMO.


> You're supposed to initialize them or assign them
> to appropriate values before using them.

sure, but if they always default to _usable_ constants no 
expectations are lost and no bugs are created.


> Now, since the default values are
> well-known and well-defined, you can rely on them if you 
> actually _want_ those
> values,

yes, and how often do you _want_ a NaN in the mix? You can rely 
on usable values just as much. Even more so since Ints and Floats 
would be consistent.


> but the whole purpose of default initialization is to make code 
> fail
> deterministically when variables aren't properly initialized - 
> and to fail as
> quickly as possible.

that only makes sense in C/C++ where value are implicitly garbage 
and mess things up.


Again, this is only my perspective. I would love to hear 
convincing arguments to how great D currently defaulting to NaN 
is, and how much headache (I never knew I had) it will save me... 
but I just don't see it. In fact I'm now more convinced of the 
opposite. Never in C# have I ran into issues with unexpected 
values from default initializers. Most important values are set 
at runtime through object constructors; not at declaration.
April 14, 2012
Re: floats default to NaN... why?
On Saturday, 14 April 2012 at 05:19:38 UTC, dennis luehring wrote:
> Am 14.04.2012 06:00, schrieb F i L:
>>      struct Foo {
>>        int x, y;    // ready for use.
>>        float z, w;  // messes things up.
>>        float r = 0; // almost always...
>>      }
>
> how often in your code is 0 or 0.0 the real starting point?
> i can't think of any situation except counters or something
> where 0 is a proper start - and float 0.0 is in very very few 
> cases a normal start - so whats your point?

Every place that a structure property is designed to be mutated 
externally. Almost all Math structures, for instance.

Defaults are to combat garbage values, but debugging cases where 
values where accidentally unset (most likely missed during 
construction) seems like a better job for a unittest.
April 14, 2012
Re: floats default to NaN... why?
On Saturday, April 14, 2012 07:41:33 F i L wrote:
> > You're supposed to initialize them or assign them
> > to appropriate values before using them.
> 
> sure, but if they always default to _usable_ constants no
> expectations are lost and no bugs are created.

No. You always have a bug if you don't initialize a variable to the value that 
it's supposed to be. It doesn't matter whether it's 0, NaN, 527.1209823, or 
whatever. All having a default value that you're more likely to use means is 
that you're less likely to have to explicitly initialize the variable. It has 
to be initialized to the correct value regardless. And if you're in the habit 
of always initializing variables and never relying on the defaults if you can 
help it, then the cases where variables weren't initialized to what they were 
supposed to be stand out more.

> > but the whole purpose of default initialization is to make code
> > fail
> > deterministically when variables aren't properly initialized -
> > and to fail as
> > quickly as possible.
> 
> that only makes sense in C/C++ where value are implicitly garbage
> and mess things up.

??? D was designed with an eye to improve on C/C++. In C/C++, variables aren't 
guaranteed to be initialized, so if you forget to initialize them, you get 
garbage, which is not only buggy, it results in non-deterministic behavior. 
It's always a bug to not initialize a variable. D's approach is to say that 
it's _still_ a bug to not initialize a variable, since you almost always need 
to initialize a variable to something _other_ than a default. But rather than 
leaving them as garbage, D makes it so that variables are default-initialized, 
making the buggy behavior deterministic. And since not initializing a variable 
is almost always a bug, default values which were the closest to error values 
for each type were chosen.

You can disagree with the logic, but there it is. I don't see how it's an 
issue, since you almost always need to initialize variables to something other 
than the default, and so leaving them as the default is almost always a bug.

The only point of dispute that I see in general is whether it's better to rely 
on the default or to still explicitly initialize it when you actually want the 
default. Relying on the default works, but by always explicitly initializing 
variables, those which are supposed to be initialized to something other than 
the defaults but aren't are then much more obvious.

Regardless, the _entire_ reason for default-initialization in D revolves 
around making buggy initializations deterministic and more detectable.

- Jonathan M Davis
April 14, 2012
Re: floats default to NaN... why?
On 04/13/2012 09:00 PM, F i L wrote:

> default is NaN

Just to complete the picture, character types have invalid initial 
values as well: 0xFF, 0xFFFF, and 0x0000FFFF for char, wchar, and dchar, 
respectively.

Ali
April 14, 2012
Re: floats default to NaN... why?
On Friday, April 13, 2012 23:29:40 Ali Çehreli wrote:
> On 04/13/2012 09:00 PM, F i L wrote:
>  > default is NaN
> 
> Just to complete the picture, character types have invalid initial
> values as well: 0xFF, 0xFFFF, and 0x0000FFFF for char, wchar, and dchar,
> respectively.

Yeah. Thanks for mentioning those. I keep forgetting to list them whenever 
default values get discussed...

- Jonathan M Davis
April 14, 2012
Re: floats default to NaN... why?
F i L wrote:

> It sounds like circular reasoning.

Several considerations pressed the design into the current form:
1) always changing output on unchanged input is hard to debug
2) GC needs to be saved from garbage, that looks like pointers
3) missed explicit initializations should not create syntax errors
4) use hardware signalling to overcome some of the limitations 
impressed by 3).
5) more???

For me the only questionable point is numer three.

-manfred
April 14, 2012
Re: floats default to NaN... why?
Jonathan M Davis wrote:
> No. You always have a bug if you don't initialize a variable to 
> the value that
> it's supposed to be. It doesn't matter whether it's 0, NaN, 
> 527.1209823, or
> whatever. All having a default value that you're more likely to 
> use means is
> that you're less likely to have to explicitly initialize the 
> variable. It has
> to be initialized to the correct value regardless.

Yes, I'm in favor of default values. That's not my argument here. 
I'm saying it makes more sense to have the default values be 
_usable_ (for convenience) rather than designed to catch 
(**cause**) bugs.


> And if you're in the habit
> of always initializing variables and never relying on the 
> defaults if you can
> help it,

That seams like a very annoying habit requirement for a languages 
with a focus on "modern convenience". What's more bug prone, I 
think, is forcing developers to remember that unset floats 
(specifically) will cause bugs while it's neighboring int works 
perfectly without a explicit value.


> then the cases where variables weren't initialized to what they 
> were
> supposed to be stand out more.

Usable defaults don't need to stand out because they're usable 
and deterministic. If you want to make sure a constructor/method 
produces expected results, unittest it.. it makes more sense to 
catch errors at compile time anyways.




> D's approach is to say that
> it's _still_ a bug to not initialize a variable, since you 
> almost always need
> to initialize a variable to something _other_ than a default.

Not always, but that's besides the point. The point is that in 
the places where you do want zero values (Vector/Matrix/etc, 
Polygon structs, counters, etc..) it's better to have consistent 
expected behavior from the default value. Not some value that 
causes runtime bugs.


> I don't see how it's an
> issue, since you almost always need to initialize variables to 
> something other
> than the default, and so leaving them as the default is almost 
> always a bug.

To me, it's not a huge issue, only an annoyance. However I 
wouldn't underestimate the impact of bad marketing. When people 
are trying out the language, and they read "variables are 
defaulted, not garbage" do you think they're going to expect ints 
and floats to work in different ways?

And it doesn't cause bugs to default value types to zero. I have 
enough experience with C# to know that it doesn't. All it does is 
make the language more consistent.


> The only point of dispute that I see in general is whether it's 
> better to rely
> on the default or to still explicitly initialize it when you 
> actually want the
> default.

This sounds like an argument for C++. Explicit declaration isn't 
a guard against bugs, you'll still be hunting through code if 
something is incorrectly set.

The fact of the matter is default initialization _does_ happen in 
code, no matter how philosophically correct "always explicitly 
define value" might be. Unless that's enforced, it can and should 
be expected to happen. That given, it's more convenient to have 
consistent value type behavior. Float is a Value type and 
shouldn't be subjective to the security concerns of Reference 
types.


> Regardless, the _entire_ reason for default-initialization in D 
> revolves
> around making buggy initializations deterministic and more 
> detectable.

The same way string, and int are defaulted to usable values, 
float should be as well. Catching code that works with null 
pointers is one thing. Working with a value type without 
requiring the programmer have deeper knowledge about D's 
asymmetrical features is another.

If D doesn't accommodate entering Laymen, how does it expect to 
gain popularity in any major way? Efficiency puts D on the map, 
convenience is what brings the tourists.

I'm not trying to convince you that the sky is falling because I 
disagree with D's direction with floats.. just if the goal is to 
increase D's popularity, little things may turn heads away 
quicker than you think. My original post was inspired by me 
showing my D code to another C# guy earlier, and coming up with 
poor explanations as to why floats where required to be defaulted 
in my math lib. His reaction what along the lines of my first 
post.
« First   ‹ Prev
1 2 3 4 5
Top | Discussion index | About this forum | D home