August 20, 2012
On Sunday, 19 August 2012 at 22:22:28 UTC, Walter Bright wrote:
> > I find it more likely that the NaN will go unnoticed and
> > cause rare bugs.
>
> NaNs in your output are pretty obvious. For example, if your accounting program prints "NAN" for the amount on the payroll cheques, someone is guaranteed to notice. But if it's a few cents off in your disfavor, it might take you years to discover there's a problem.
>
> Critical systems also would find a NaN command a lot easier to detect than an off-by-two command, and would be able to shut down and engage the backup.

The problem is that it's easy for even NaN's to be filtered out.

float x = 0.0f;
float y; // oops
float z = min(x, y); // NaN has disappeared, unnoticed!

My argument is that conservative compile time errors on uninitialised variables are more likely to catch these errors.
August 20, 2012
On Monday, 20 August 2012 at 19:28:33 UTC, Peter Alexander wrote:
> On Sunday, 19 August 2012 at 22:22:28 UTC, Walter Bright wrote:
>> > I find it more likely that the NaN will go unnoticed and
>> > cause rare bugs.
>>
>> NaNs in your output are pretty obvious. For example, if your accounting program prints "NAN" for the amount on the payroll cheques, someone is guaranteed to notice. But if it's a few cents off in your disfavor, it might take you years to discover there's a problem.
>>
>> Critical systems also would find a NaN command a lot easier to detect than an off-by-two command, and would be able to shut down and engage the backup.
>
> The problem is that it's easy for even NaN's to be filtered out.
>
> float x = 0.0f;
> float y; // oops
> float z = min(x, y); // NaN has disappeared, unnoticed!
>
> My argument is that conservative compile time errors on uninitialised variables are more likely to catch these errors.

I just tried this:

float a, b = 10;
writeln(min(a, b), ", ", fmin(a, b));

Result:
nan, 10

I think that is incorrect - both should give NaN. The scientific viz software I use at work returns NaN for any numerical operation on NaN values, means, smoothing, etc.
August 20, 2012
On Monday, 20 August 2012 at 20:21:12 UTC, cal wrote:
> I just tried this:
>
> float a, b = 10;
> writeln(min(a, b), ", ", fmin(a, b));
>
> Result:
> nan, 10
>
> I think that is incorrect - both should give NaN. The scientific viz software I use at work returns NaN for any numerical operation on NaN values, means, smoothing, etc.

It's tricky. The only way (that I'm aware of) to get it to return NaN is to explicitly test for NaN, introducing overhead that is unnecessary 99.9% of the time.

Unfortunately this is one of those situations where D's design goals of efficiency and safety are at odds. You can't have it both ways.
August 20, 2012
Peter Alexander:

> It's tricky. The only way (that I'm aware of) to get it to return NaN is to explicitly test for NaN, introducing overhead that is unnecessary 99.9% of the time.
>
> Unfortunately this is one of those situations where D's design goals of efficiency and safety are at odds. You can't have it both ways.

Few possibilities:
1) Add a second NaN-aware function pair nmax()/nmin();
2) Add a debug{} inside min()/max(), where NaNs are managed;
3) Introduce a if(version=NaNAware){} block inside the normal max()/min() and other Phobos functions, to be used when you want more NaN-correct results.

I prefer the third idea, but this probably requires a recompilation of Phobos.

Bye,
bearophile
August 21, 2012
On 8/18/2012 9:05 PM, Nick Sabalausky wrote:

> No offense taken (or intended), but I think you're conflating the
> different branches of the discussion.

I'm glad. And it's entirely likely that I missed something. I didn't read the whole thread, I was just replying to your one message. Lazy...

> The "NaN-initing better than garbage/zero-initing" was a key
> point of the OP, yes, but not of all branches of discussion, and not
> the only point in the article either.
>
> Please reread the thread and notice that the branch I was replying to
> was directed specifically at this section of the article:
>
> --------------------------------------
> Given the code:
>
>      float f;
>      if (condition)
>          ++f;
>
> the user writes "in language X, the compiler complains on line 3 saying
> that f is used without being initialized. Isn't that a better
> solution?" For that example, it probably is a better solution. But
> consider:
>
>      float f;
>      if (condition1)
>          f = 7;
>      ... code ...
>      if (condition2)
>          ++f;
>
> [Goes on attempting to build a case against the static checking]
> --------------------------------------
>
> So yes, the OP *IS* claiming "NaN-initing > conservative static checks"
> and that is what this branch of the thread, including the post I
> directly replied to, was directly addressing.

Fair enough. I agree with you that the claim is in error -- not because I think the > operator should be pointing <, but because the two things are unrelated and shouldn't be compared. One is a compile-time check, the other a runtime minefield. One is a logic analysis, the other an out-of-domain value test. They are not XOR -- *both* should be used. (And some of us clever programmers will slip bogus programs past both of them anyway...)

>> Yes, it would be great if the D compiler (or a C++11 compiler, or C#
>> or Scala or what have you) could do a complete static check...
>
> Actually, I disagree. I want a static check, but I *don't* want it to
> be complete. Largely because of the difficulty of doing so and the
> compile times, yes, BUT also because (as I already said) code such as
> this:
>
>      float f;
>      if (condition)
>          ++f;
>      // Complex stuff that may involve f in certain codepaths
>
> Is *already* fragile and bad, and could easily hide/generate bugs even
> with NaN. In fact, there's a reasonable chance it may already be a bug,
> again even with NaN. And even with NaN, it would still be much better to
> just get an error for the whole damn thing: "ERROR: This is screwy
> fucking code. Even if it's not technically buggy right now, which is
> questionable anyway, it can easily become a bug if it gets altered
> without being very, VERY careful. So go back and rewrite it to not be so
> damned fragile."

So true. But the only technology I have found to date capable of such insight is a good code review. And a good reviewer who is willing to take the time and analyze my code in depth (and then patiently and politely get past my mule-headed stubbornness and get me to see what he's saying) is a treasure beyond price. Man, if you can package *that* in a compiler, I'm buyin'!

>> Consider how useful *integer* NaN is. Oh, you didn't realize that,
>
> Please don't put words in my mouth. I've advocated in past discussions
> for making 'int.init' be 0xDEADBEEF or 0x69696969 as a
> "next-best thing" for when (as with D) static checking isn't performed.

I apologize. I did not mean that *you personally* were advocating specific "not an integer" values like -1. I meant the generic "you" as in many APIs that I am sure you are familiar with. Certainly there may be applications where a human-recognizable "dead beef" flag works well... and others where you don't want to drop a "not a value" value into the middle of a useful range. (0xDEADBEEF is not a useful value, but 0xDEADBEEE and 0xDEADBEF0 are??)

What I was trying to say was that programmers re-invent the "not a valid value" trick all the time. For example, the Unicode Consortium defines the code point 0xFFFE to be an invalid character, and thus allows tricks like the Byte Order Mark. In my own specialty of Windows installer work, a zero exit code marks "success" and a non-zero exit is "failure"... except that the special values of decimal 3010 and (archaically) 8192 flag "success but reboot required to complete the install". NULL and nullptr are special values for C/C++. And so on.

I have needed a flag to indicate a Boolean with a one-time initialization often enough to have written a small class to encapsulate the True|False|NotYetSet behavior. And it lacks rigor. For instance, it does not have the "taints all downstream expressions" behavior that floating NaN does. One of these days I will rewrite it correctly and add it to a library.

>> ...So having a hardware NaN in
>> floating point, particularly one that "taints" all future results it
>> participates in, and further one that can (by definition) never be a
>> legitimate number, is genius. And having Walter design D to take
>> advantage of it is... well, perhaps not genius, but damned smart.
>
> Right. But what's *even smarter* than that, is just eliminating the
> whole problem at compile-time. Walter has specifically argued
> against the wisdom of that on various occasions (including in this
> article), and what I'm saying is that I don't buy *that* reasoning or
> its conclusion. (And then I went on and bitched about a previous
> discussion where he kept trying to use "NaN/0-init > garbage-init" as a
> rebuttal to my completely *different* argument of "static checks >
> NaN/0-init". Hence the "strawman".)

OK. Well, you may need to write your own compiler, then. You and Walter may be enlightened enough to gain insight through earnest reason together. Me, you usually have to hit me over the head with something heavy a couple of times before I get your point. <grin> Ouch.

-- Davidson

August 21, 2012
On Monday, 20 August 2012 at 19:28:33 UTC, Peter Alexander wrote:
[cut]
> The problem is that it's easy for even NaN's to be filtered out.
>
> float x = 0.0f;
> float y; // oops
> float z = min(x, y); // NaN has disappeared, unnoticed!


In theory, one could prevent this by setting the floating point control word to trigger a trap when reading a NaN which I think *should* be the default.

Unfortunately it doesn't seem to work, there is an FPE in the code below, where it shouldn't.
Then again, the online compiler I've tried is very old: dmd-2.042.

>>
import std.stdio, std.math, std.algorithm;

int main()
{
FloatingPointControl fpc;
fpc.enableExceptions(FloatingPointControl.severeExceptions);

float x = 0.0f;
float y; // oops
y=1;
float z = min(x, y);

writeln("min x,y=",z);

return 0;
}
<<






August 21, 2012
On 20/08/12 22:21, cal wrote:
> On Monday, 20 August 2012 at 19:28:33 UTC, Peter Alexander wrote:
>> On Sunday, 19 August 2012 at 22:22:28 UTC, Walter Bright wrote:
>>> > I find it more likely that the NaN will go unnoticed and
>>> > cause rare bugs.
>>>
>>> NaNs in your output are pretty obvious. For example, if your
>>> accounting program prints "NAN" for the amount on the payroll
>>> cheques, someone is guaranteed to notice. But if it's a few cents off
>>> in your disfavor, it might take you years to discover there's a problem.
>>>
>>> Critical systems also would find a NaN command a lot easier to detect
>>> than an off-by-two command, and would be able to shut down and engage
>>> the backup.
>>
>> The problem is that it's easy for even NaN's to be filtered out.
>>
>> float x = 0.0f;
>> float y; // oops
>> float z = min(x, y); // NaN has disappeared, unnoticed!
>>
>> My argument is that conservative compile time errors on uninitialised
>> variables are more likely to catch these errors.
>
> I just tried this:
>
> float a, b = 10;
> writeln(min(a, b), ", ", fmin(a, b));
>
> Result:
> nan, 10
>
> I think that is incorrect - both should give NaN. The scientific viz
> software I use at work returns NaN for any numerical operation on NaN
> values, means, smoothing, etc.

No, it's the other way around.
The IEEE 754 standard defines min(x, NaN) == min(NaN, x) == x.

According to the C standard, fmin() should be returning 10, as well.
There is a bug in fmin().

However min() and max() are extremely unusual in this respect. Almost everything else involving a NaN returns NaN.



August 21, 2012
On 08/20/2012 11:09 AM, Andrej Mitrovic wrote:
> On 8/20/12, Walter Bright<newshound2@digitalmars.com>  wrote:
>> On 8/19/2012 6:33 PM, Chad J wrote:
>>> How?  I remember reading a lot of material on NaNs in D, but I don't
>>> recall these.
>>
>> Set the control word for the x87 to turn it on. Probably have to do a little
>>
>> inline assembly.
>>
>
> I thought you could do by setting FloatingPointControl in std.math.

That looks much more convenient and clean.

Gotta make the right thing easy to do, right? <g>

How thread-local is it?


August 21, 2012
On Tuesday, 21 August 2012 at 12:25:45 UTC, Chad J wrote:
> On 08/20/2012 11:09 AM, Andrej Mitrovic wrote:
>> On 8/20/12, Walter Bright<newshound2@digitalmars.com>  wrote:
>>> On 8/19/2012 6:33 PM, Chad J wrote:
>>>> How?  I remember reading a lot of material on NaNs in D, but I don't
>>>> recall these.
>>>
>>> Set the control word for the x87 to turn it on. Probably have to do a little
>>>
>>> inline assembly.
>>>
>>
>> I thought you could do by setting FloatingPointControl in std.math.
>
> That looks much more convenient and clean.
>
> Gotta make the right thing easy to do, right? <g>

Except that it doesn't seem to work as expected (maybe an old compiler's bug..)

RenoX


>
> How thread-local is it?


August 21, 2012
On 21/08/12 16:14, renoX wrote:
> On Tuesday, 21 August 2012 at 12:25:45 UTC, Chad J wrote:
>> On 08/20/2012 11:09 AM, Andrej Mitrovic wrote:
>>> On 8/20/12, Walter Bright<newshound2@digitalmars.com>  wrote:
>>>> On 8/19/2012 6:33 PM, Chad J wrote:
>>>>> How?  I remember reading a lot of material on NaNs in D, but I don't
>>>>> recall these.
>>>>
>>>> Set the control word for the x87 to turn it on. Probably have to do
>>>> a little
>>>>
>>>> inline assembly.
>>>>
>>>
>>> I thought you could do by setting FloatingPointControl in std.math.
>>
>> That looks much more convenient and clean.
>>
>> Gotta make the right thing easy to do, right? <g>
>
> Except that it doesn't seem to work as expected (maybe an old compiler's
> bug..)

Yeah, there's a bug in the code generation, at the moment for 80-bit reals it works on AMD but not Intel processors (!)