August 13, 2020
On Thursday, 13 August 2020 at 10:10:34 UTC, H. S. Teoh wrote:
> On Thu, Aug 13, 2020 at 07:22:18AM +0000, mw via Digitalmars-d wrote: [...]
>> void main() {
>>   long   a = -5000;
>>   size_t b = 2;
>>   long   c = a / b;
>>   writeln(c);
>> }
>
> You're mixing signed and unsigned values. That's generally dangerous territory where integer promotion rules inherited from C/C++ take over and cause sometimes weird effects, like here. Changing integer promotion rules will probably never happen now, because it will cause massive *silent* breakage of existing code. So, in the spirit of defensive programming, I recommend avoiding mixing signed/unsigned values in this way.
>
>
> T

Changing integer promotion rules to disallow promotion of signed to unsigned for division will not cause massive silent breakage. - But it will cause massive visible breakage; Phobos uses this all over.
August 13, 2020
On Thursday, 13 August 2020 at 10:10:34 UTC, H. S. Teoh wrote:

> So, in the spirit of defensive programming, I recommend avoiding mixing signed/unsigned values in this way.

This is not a practical solution. [1, 2, 3, 4].length returns ulong, which guarantees this type of mixing goes on without people even realizing it's in their code base.

Imagine a new user to the language wanting to compute the mean of an array of numbers:

import std;
void main()
{
    long sum = 0;
    long[] vec = [-112, 2, 23, -4];
    foreach(val; vec) {
        sum += val;
    }
    writeln(sum/vec.length);
}

This is inexcusable.
August 13, 2020
On Thursday, 13 August 2020 at 10:40:33 UTC, bachmeier wrote:
> 
> [snip]
>
> Imagine a new user to the language wanting to compute the mean of an array of numbers:
>
> import std;
> void main()
> {
>     long sum = 0;
>     long[] vec = [-112, 2, 23, -4];
>     foreach(val; vec) {
>         sum += val;
>     }
>     writeln(sum/vec.length);
> }
>
> This is inexcusable.

It's certainly annoying, but if there were an equivalent length function in C, then it would have the same behavior. Unfortunately, this is one of those things that were carried over from C.

Also, note that the true mean of vec above is -22.75, which wouldn't even be the result of your function if length returned a signed variable, because you would be doing integer division. A person who comes to D without ever having programmed before would get tripped up by that too.

A new user to the language can use mir's mean function:
http://mir-algorithm.libmir.org/mir_math_stat.html#.mean

import mir.math.stat: mean;

void main()
{
    long[] vec = [-112, 2, 23, -4];
    assert(vec.mean == -22.75);
}
August 13, 2020
This has been ongoing since almost the beginning.

https://issues.dlang.org/show_bug.cgi?id=259

It might be the oldest filed D bug now (just checked, it is). Of course, because Walter detests warning messages no resolution will probably ever be made. The only thing Walter contributed to that issue is trying to close it without resolving it.

Here's to 14 years.


August 13, 2020
On Thursday, 13 August 2020 at 11:40:59 UTC, jmh530 wrote:
> On Thursday, 13 August 2020 at 10:40:33 UTC, bachmeier wrote:
>> 
>> [snip]
>>
>> Imagine a new user to the language wanting to compute the mean of an array of numbers:
>>
>> import std;
>> void main()
>> {
>>     long sum = 0;
>>     long[] vec = [-112, 2, 23, -4];
>>     foreach(val; vec) {
>>         sum += val;
>>     }
>>     writeln(sum/vec.length);
>> }
>>
>> This is inexcusable.
>
> It's certainly annoying, but if there were an equivalent length function in C, then it would have the same behavior. Unfortunately, this is one of those things that were carried over from C.

The source of wrong behavior is vec.length having type ulong. It would be very unusual for someone to even think about that.

> Also, note that the true mean of vec above is -22.75, which wouldn't even be the result of your function if length returned a signed variable, because you would be doing integer division. A person who comes to D without ever having programmed before would get tripped up by that too.

That's why that behavior needs to be changed as well. It's horrible to implicitly cast from int to double when doing so results in obviously wrong behavior. Hopefully there won't be any more talk about safe by default as long as the language has features like this that are obviously broken and trivially fixed.
August 13, 2020
On Thursday, 13 August 2020 at 13:33:19 UTC, bachmeier wrote:
> That's why that behavior needs to be changed as well. It's horrible to implicitly cast from int to double when doing so results in obviously wrong behavior. Hopefully there won't be any more talk about safe by default as long as the language has features like this that are obviously broken and trivially fixed.

It's not trivially fixed. :-(

I added a check for this case in DMD, just to see, and it breaks Phobos all over. Anything that interfaces to C with more complicated struct types does division with mixed signs. There'd need to be a lot of casts added as a result of changing this.
August 13, 2020
On Thursday, 13 August 2020 at 13:33:19 UTC, bachmeier wrote:
> 
>> Also, note that the true mean of vec above is -22.75, which wouldn't even be the result of your function if length returned a signed variable, because you would be doing integer division. A person who comes to D without ever having programmed before would get tripped up by that too.
>
> That's why that behavior needs to be changed as well. It's horrible to implicitly cast from int to double when doing so results in obviously wrong behavior.

I'm a little confused by this. My point was that the way D works now is that there is no error in the below code.
assert(-91 / 4 == -22);
even though the result is not the mathematically correct one (-22.75). There is no implicit cast to double in this case. Modifying it to
assert(-91.0 / 4 == -22.75);
gives the right result, but if you want to be more explicit and start from a signed variable and an unsigned variable, you would do
assert(cast(double) -91 / cast(double) 4u == -22.75);



August 13, 2020
On Thursday, 13 August 2020 at 09:25:07 UTC, John Colvin wrote:

> Personally I would love to be able to disallow implicit signed-to-unsigned conversions in almost all cases, but that would be a big breaking change.

At least I want a warning message, even with a turn-on command-line switch is fine, I personally will turn it on all the time, silently performing this conversions is horrible.

I spent half of the night yesterday to trace down the issue.

https://run.dlang.io/is/je8L4r
==================================
import std.algorithm;
import std.stdio;

void main() {
  long[] a = [-5000, 0];
  long   c = sum(a) / a.length;
  writeln(c);   // output 9223372036854773308
}
==================================


In contrast, there was a compiler warning message for loop index
https://run.dlang.io/is/4Sqc5n
onlineapp.d(4): Deprecation: foreach: loop index implicitly converted from size_t to int


August 13, 2020
On 8/13/20 6:33 AM, bachmeier wrote:

> The source of wrong behavior is vec.length having type ulong.

I remember watching C++ experts on an "ask us anything" kind of session at a conference. They were agreeing that it was a mistake that std::vector::size returns unsigned. (Herb Sutter: "We were wrong.")

Ali

August 13, 2020
On Thursday, 13 August 2020 at 13:33:19 UTC, bachmeier wrote:
> ...
> The source of wrong behavior is vec.length having type ulong. It would be very unusual for someone to even think about that.

May I ask what type should it be?

Matheus.