August 21, 2022

On Sunday, 21 August 2022 at 03:25:57 UTC, Walter Bright wrote:

>

On 8/20/2022 6:19 AM, Hipreme wrote:

>

I don't see how easier it is to track a nan. Division by zero causes exception which is the best thing ever. Multiplication produces a zero result, which is pretty obvious to track. If the number does not change you will pretty much print both values and you'll easily find the 0 there.

x * NaN => NaN
x / NaN => NaN
x + NaN => NaN
x - NaN => NaN
-Nan => NaN
cos(NaN) => NaN
exp(NaN) => NaN

Only a subset of this is true for 0.

Imagine, we have nullable int (int?) in language.

int? x => null
x * null => null
x / null => null
x + null => null
x - null => null
-cast(int?)null => null

Seems like perfect solution (except performance costs)

August 21, 2022
On 8/21/22 06:25, Walter Bright wrote:
> On 8/20/2022 6:19 AM, Hipreme wrote:
>> I don't see how easier it is to track a nan. Division by zero causes exception which is the best thing ever. Multiplication produces a zero result, which is pretty obvious to track. If the number does not change you will pretty much print both values and you'll easily find the 0 there.
> 
> x * NaN => NaN
> x / NaN => NaN
> x + NaN => NaN
> x - NaN => NaN
> -Nan => NaN
> cos(NaN) => NaN
> exp(NaN) => NaN
> 
> Only a subset of this is true for 0.
> 

I totally agree that NaN is really useful because makes it easier to find the reason for the NaN result
August 21, 2022

On Sunday, 21 August 2022 at 11:13:02 UTC, Dark Hole wrote:

>

Imagine, we have nullable int (int?) in language.

int? x => null
x * null => null
x / null => null
x + null => null
x - null => null
-cast(int?)null => null

Seems like perfect solution (except performance costs)

We have this in the standard library already:

import std.checkedint;

Checked!(int, WithNaN) n;

assert(n.isNaN);
assert((123 * n).isNaN);
assert((123 / n).isNaN);
assert((123 + n).isNaN);
assert((123 - n).isNaN);
assert((-n).isNaN);
August 21, 2022

On Sunday, 21 August 2022 at 13:47:43 UTC, Paul Backus wrote:

>

On Sunday, 21 August 2022 at 11:13:02 UTC, Dark Hole wrote:

>

Imagine, we have nullable int (int?) in language.

int? x => null
x * null => null
x / null => null
x + null => null
x - null => null
-cast(int?)null => null

Seems like perfect solution (except performance costs)

We have this in the standard library already:

import std.checkedint;

Checked!(int, WithNaN) n;

assert(n.isNaN);
assert((123 * n).isNaN);
assert((123 / n).isNaN);
assert((123 + n).isNaN);
assert((123 - n).isNaN);
assert((-n).isNaN);

My point is making this by default for all primitive types. This will be consistent and simple:

bool x; // Error, use bool? or init it something meaningful
struct Foo { bool x; } // Error too

So we have no problem "programmers make meaningless 0" and have no problem to determine init for most of types.

August 21, 2022
On 8/21/2022 2:01 AM, claptrap wrote:
> So from my experience the rational is based on two fallacies.
> 
> 1. That using zero as default init would hide the bug.

If the computed answer is 1.7239 rather than 1.7230 because of an erroneous 0 input, how would you know it is wrong? But if the computed answer is NaN, you *know* it is wrong.


> 2. That people will just stick zero in because they don't know what it should be.

I know *you* wouldn't do it (I wouldn't either), but like I wrote, I've seen it happen. I've also seen cases of:

    double x = 0;
    ... no uses of x ...
    x = 5;
    ...

where the initialization was put in to shut the compiler up. But to one unfortunate enough to be reviewing unfamiliar code, this double initialization has a smell about it - which initialization is the correct one? Why was it initialized to 0 when that makes no sense for the algorithm?

It is also why if it is desirable for a variable to be uninitialized, the

    double x = void;

form must be used, as that is highly unlikely to be written by accident.

I.e. this is all designed to encourage the programmer to write code intentionally, rather than accidentally.

BTW, I've done user support (helping them debug problems with their code) for 40 years now. I've talked a lot with team managers about what problems they experience with code. A lot of D's unusual features come from this experience.
August 21, 2022
On 8/20/2022 11:23 PM, Mike Parker wrote:
> On Sunday, 21 August 2022 at 03:26:55 UTC, Walter Bright wrote:
>> On 8/20/2022 6:19 AM, Hipreme wrote:
>>> Drawing with float is required a lot on modern drawing API.
>>
>> I find this surprising. My experience with floating point coordinates is you can never quite get the pixels aligned, due to rounding problems.
> 
> Graphics APIs have been using floating point for decades now. GPUs are optimized for it. You'll still see 2D APIs around now and again with an integer-based public API, but internally they're sending floats to the GPU.

I guess I stand corrected :-)
August 21, 2022
On 8/20/2022 10:03 PM, Ali Çehreli wrote:
> On 8/20/22 20:44, Walter Bright wrote:
> 
>  > I.e. a "payload" can be put in the mantissa bits of a NaN, which can be
>  > used to provide more information about the NaN.
> 
> I think it is called "nan boxing."

BTW, this influences code generation, too. For example:

    x = 0;
    y = NaN;
    z = x + y;

z gets assigned a NaN. But which NaN? The NaN that was assigned to x. I.e. the specific NaN values get propagated.
August 21, 2022
On Sunday, 21 August 2022 at 15:26:54 UTC, Walter Bright wrote:
> On 8/21/2022 2:01 AM, claptrap wrote:
>> So from my experience the rational is based on two fallacies.
>> 
>> 1. That using zero as default init would hide the bug.
>
> If the computed answer is 1.7239 rather than 1.7230 because of an erroneous 0 input, how would you know it is wrong? But if the computed answer is NaN, you *know* it is wrong.

Go through all the math functions in phobos and see what would happen if you zeroed out some random local variables, you wont get results that are a tiny fraction of a percent out, it'll be way off 99 times out of 100.

Im not saying it cant happen, but that it's vastly exaggerated how likely it is that an erroneous zero init wont be obvious.


>> 2. That people will just stick zero in because they don't know what it should be.
>
> I know *you* wouldn't do it (I wouldn't either), but like I wrote, I've seen it happen. I've also seen cases of:
>
>     double x = 0;
>     ... no uses of x ...
>     x = 5;
>     ...
>
> where the initialization was put in to shut the compiler up. But to one unfortunate enough to be reviewing unfamiliar code, this double initialization has a smell about it - which initialization is the correct one? Why was it initialized to 0 when that makes no sense for the algorithm?

Id still rather have the compiler error, that is more helpful to me that preventing somebody else writing crappy but *working* code.


August 21, 2022
Consider the following pattern, which doesn't appear frequently, but frequently enough:

    double x;
    if (condition) {
        x = 5;
        ...
    }
    ...               // (1)
    if (condition) {
       foo(x);
    }

Imagine there's a lot more code omitted which obscures the pattern. This code is correct.

Now, maintainer adds `bar(x)` at (1).

The scenarios:

1. x is default initialized to NaN. bar(x) produces a NaN result on everything dependent on x. User knows there's a problem.

2. x is default initialized to 0. bar(0) may exhibit problems, but these problems won't necessarily be noticed.

3. compiler complains that `double x;` needs an initializer. To shut up the compiler, the user initializes it to 0, without putting much thought into it. bar(0) may exhibit problems, but these won't necessarily be noticed.

4. compiler complains that `double x;` needs an initializer. Coder just schlepps in a 0. Yes, this happens. Maintainer wastes time wondering why x is initialized to 0, as that may be a nonsense value for x. Maintainer wonders if this unused initialization has a purpose, maybe it is the result of a bad refactoring? Wastes more time investigating it.

D chose option 1.

BTW, option 1 is very analogous to the common "Optional" types which can be either an error value or a valid value. Optional types propagate just like NaNs do.
August 21, 2022
On Sunday, 21 August 2022 at 16:51:51 UTC, Walter Bright wrote:
> Consider the following pattern, which doesn't appear
>
>
> 1. x is default initialized to NaN. bar(x) produces a NaN result on everything dependent on x. User knows there's a problem.
>
> 2. x is default initialized to 0. bar(0) may exhibit problems, but these problems won't necessarily be noticed.

This is the problem, you suggest that if a variable is zero initialised in error the problems it causes "wont necessarily" be noticed.

I'm saying that's not true, I'm saying it will almost always be noticed.