January 24, 2014
Walter Bright:

> 1. Go determines the type of (e1 op e2) as the type of the first operand.
>
>   http://golang.org/ref/spec#Arithmetic_operators
>
> I consider this not only surprising (as we expect + to be commutative) but can lead to unexpected truncation, as in (byte = byte + int32).

I don't know much about Go, but I have read some Go programs, and that comment looks suspicious. So I have created a little Go program:


package main
import ("fmt")
func main() {
    var x byte = 10;
    var y int = 1000;
    z := x + y
    fmt.Println(z)
}


If you don't have a Go compiler you can try some code here:
http://play.golang.org/

The result:
http://play.golang.org/p/iP20v0r566

It gives:
prog.go:6: invalid operation: x + y (mismatched types byte and int)

So despite what the docs say, it seems the two types need to be the same for the sum to work?

While this program compiles:


package main
import ("fmt")
func main() {
    var x byte = 10;
    var y int = 1000;
    z1 := int(x) + y
    fmt.Println(z1)
    z2 := x + byte(y)
    fmt.Println(z2)
}


And prints:
1010
242

Bye,
bearophile
January 24, 2014
On 1/23/2014 5:44 PM, bearophile wrote:
> So despite what the docs say, it seems the two types need to be the same for the
> sum to work?

I was going by what the spec said.


> While this program compiles:
>
>
> package main
> import ("fmt")
> func main() {
>      var x byte = 10;
>      var y int = 1000;
>      z1 := int(x) + y
>      fmt.Println(z1)
>      z2 := x + byte(y)
>      fmt.Println(z2)
> }

Note the casts.

January 24, 2014
On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:
>> byte a = -128;
>> auto b = -a;
>>
>> What type should b get? (of course "byte" but the value doesn't fit!)
>
> The type will be int.
Ah, ok. Of course the small types always become int.
But the problem would be the same with

long a = long.min;
auto b = -a;

does this return ulong (which could hold the correct result) or long (and a wrong result)?

>> integral types still suffer the same old flaws.
>
> There are quite a few improvements for integrals, too, most importantly of the kind that don't exact a speed penalty.
I can understand that speed is critical for unsigned types but for me the main benefit of signed types is their "ease of use" - like in the "hello world" program it should be easy to do it right and work "out of the box". Errors like

int a = 2_000_000_000;
int b = a + a;

should not generate weird stuff like -294_967_296 (which it actually does) but better produce NaN to indicate that the result is not in the valid range or "int".
For addition that may be not to complicated to handle, but for multiplication? There it would be very nice (and fast!!) to have an implenetation that checks the carry and set the result to NaN if carry is not 0. At the moment doing so requires the use of inline assembler - not realy a newbi-thing to do...
January 24, 2014
On Friday, 24 January 2014 at 10:40:46 UTC, Dominikus Dittes Scherkl wrote:
> On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:

> int a = 2_000_000_000;
> int b = a + a;
>
> should not generate weird stuff like -294_967_296 (which it

Long discussion about signed/unsigned integer overflows...
January 24, 2014
On Friday, 24 January 2014 at 11:43:08 UTC, eles wrote:
> On Friday, 24 January 2014 at 10:40:46 UTC, Dominikus Dittes Scherkl wrote:
>> On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:
>
>> int a = 2_000_000_000;
>> int b = a + a;
>>
>> should not generate weird stuff like -294_967_296 (which it
>
> Long discussion about signed/unsigned integer overflows...

But that is a HUGE source of errors, even in really carefully developed software in safety critical systems!
I think it is well worth a thought to have a safe type in the language, even if we buy it with a small performance tradeoff. Especially for the "automatic" type where the programmer has not spend much time in carefully choosing the types to be used (e.g. the code above, even with "auto" instead of "int").

--> If I write code fast, without thinking about subtleties (like e.g. the return type of main() in "hello world") I expect the compiler to do something sensible (ok, I doesn't expect if from C, but we're talking about a better language, do we?) and I don't expect highest performance.

So I would prefer to have save signed types as default and maybe new types "sbyte", "sshort", "sint" etc if I need the last bit of performance, but without automatic conversion to those unsave types.
Using fast signed types with all the over/underflow and other unsafe stuff is like manual memory management and pointers instead of GC and slices - useful to have in case you really need them, but not the default.
January 24, 2014
On Friday, 24 January 2014 at 12:25:13 UTC, Dominikus Dittes Scherkl wrote:
> On Friday, 24 January 2014 at 11:43:08 UTC, eles wrote:
>> On Friday, 24 January 2014 at 10:40:46 UTC, Dominikus Dittes Scherkl wrote:
>>> On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:
>>
>>> int a = 2_000_000_000;
>>> int b = a + a;
>>>
>>> should not generate weird stuff like -294_967_296 (which it
>>
>> Long discussion about signed/unsigned integer overflows...
>
> But that is a HUGE source of errors, even in really carefully developed software in safety critical systems!
> I think it is well worth a thought to have a safe type in the language, even if we buy it with a small performance tradeoff. Especially for the "automatic" type where the programmer has not spend much time in carefully choosing the types to be used (e.g. the code above, even with "auto" instead of "int").
>
> --> If I write code fast, without thinking about subtleties (like e.g. the return type of main() in "hello world") I expect the compiler to do something sensible (ok, I doesn't expect if from C, but we're talking about a better language, do we?) and I don't expect highest performance.
>
> So I would prefer to have save signed types as default and maybe new types "sbyte", "sshort", "sint" etc if I need the last bit of performance, but without automatic conversion to those unsave types.
> Using fast signed types with all the over/underflow and other unsafe stuff is like manual memory management and pointers instead of GC and slices - useful to have in case you really need them, but not the default.

On the Rust mailing list, there's recently been discussion about auto-promotion to BigInt in case of overflow. Maybe that's a discussion we should be having as well?
January 24, 2014
On Friday, 24 January 2014 at 13:30:06 UTC, Meta wrote:
> On the Rust mailing list, there's recently been discussion about auto-promotion to BigInt in case of overflow. Maybe that's a discussion we should be having as well?

Nice idea. But is any overflow known at compile-time?
Also really unexpected auto-type...

I had something very simple in mind:
1) get rid of the asymmetric T.min value
   that always causes problems with abs()
2) instead use this special value as NaN
3) let NaN be the init-value of the signed types
4) let every over-/underflow result in NaN
5) let every operation involving NaN result in NaN
5) let any cast from other types to the save
   signed types check range and set NaN if the
   value doesn't fit
None of that should be too expensive, but with such a type you can simply execute the program and if it result in NaN you know there had been some overflow (or uninitialized variable). That makes analyzing easy, it allows for simple contracts, is easy to catch and allows easy to decide what solution would be the best (e.g. using next bigger type or limit the values).

And if performance is critical (which should be true only in some inner loop where one can be sure that no overflow is possible) as next step the now fool-prove program can be changed to use unsave types (because they use the same range + one extra value that hopefully never occures anyway).
January 24, 2014
On Friday, 24 January 2014 at 12:25:13 UTC, Dominikus Dittes Scherkl wrote:
> On Friday, 24 January 2014 at 11:43:08 UTC, eles wrote:
>> On Friday, 24 January 2014 at 10:40:46 UTC, Dominikus Dittes Scherkl wrote:
>>> On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:

> But that is a HUGE source of errors, even in really carefully developed software in safety critical systems!

I know, I know, I do exactly that for my bread. I am on your side. Walter isn't ;)
January 24, 2014
On 1/24/2014 2:40 AM, Dominikus Dittes Scherkl wrote:
> Ah, ok. Of course the small types always become int.
> But the problem would be the same with
>
> long a = long.min;
> auto b = -a;
>
> does this return ulong (which could hold the correct result) or long (and a
> wrong result)?

The negation operator does not change the type, and no operation changes the type as the result of particular runtime operand values.

BTW, Python has what you want - runtime overflow automatically fails over to bignum. But Python is a slow language.
January 24, 2014
On Friday, 24 January 2014 at 19:03:59 UTC, Walter Bright wrote:
> On 1/24/2014 2:40 AM, Dominikus Dittes Scherkl wrote:
>> Ah, ok. Of course the small types always become int.
>> But the problem would be the same with
>>
>> long a = long.min;
>> auto b = -a;
>>
>> does this return ulong (which could hold the correct result) or long (and a
>> wrong result)?
>
> The negation operator does not change the type, and no operation changes the type as the result of particular runtime operand values.
So, was Andrei wrong as he claimed b would be "int" in my first example?

byte a = -128;
auto b = -a;

Or is changing byte to int no typechange?!?

> BTW, Python has what you want - runtime overflow automatically fails over to bignum. But Python is a slow language.
It was someone else who wanted to start a discussion over such a feature in D.

I'm fine with the safe signed type I've developed.
The only flaw in my struct for now is that I'm not able to overload opCmp in such a way that NaN compared to anything else would always be false (either < or >= is true because they can't be overloaded separately) :-/
But checking for NaN before any calculation is always the better way, so that flaw hits not too hard.
I will do some performance checks to see how "slow" this will make some heavy calculations in reality.