January 24, 2014
On 1/24/2014 1:39 PM, Dominikus Dittes Scherkl wrote:
> 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?

No.

1. a was promoted to int before the negation operator was applied.

2. types do not depend on particular runtime values (the whole notion of static typing would fall apart if it did)

January 24, 2014
On 01/24/2014 11:33 PM, Walter Bright wrote:
> ...
> 2. types do not depend on particular runtime values (the whole notion of
> static typing would fall apart if it did)

http://en.wikipedia.org/wiki/Dependent_type
January 24, 2014
On 1/24/14 2:40 AM, Dominikus Dittes Scherkl wrote:
> 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)?

long

>>> 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...

There's no NaN for integrals.

I initially protested a number of things about the way D's integral expressions are handled. For example I found it ridiculous that unary "-" for uint returns uint. Walter talked me into accepting the C-style rules and improving them with value range propagation.


Andrei

January 24, 2014
On 1/24/14 6:08 AM, Dominikus Dittes Scherkl wrote:
> 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).

The only reason that NaN works in the FP world is that it's done in hardware as part of existing operations.  For it to work in the integer world it'd require adding operations to do the extra checking and handling with the resulting performance costs of doing so.  It's, pretty much by definition, too expensive for a set of people and the apps they care about.  The only practical way is to introduce a new type that behaves like this and use that instead.  If, over the course of years, becomes popular enough, it's not impossible that some cpu makers could decide to enshrine it in new instructions.  But I wouldn't hold your breath. :)

None of this is new.  It comes up periodically and ends up in the typical place of many feature requests, unimplemented.  Create the type, share it, see who uses it and how much they gain from the benefits and if they're worth the costs.  Conjecture will only get you so far.

My 2 cents,
Brad

January 25, 2014
On Thursday, 23 January 2014 at 20:35:56 UTC, Andrei Alexandrescu wrote:
> On 1/23/14 4:09 AM, Dominikus Dittes Scherkl wrote:
>> There is one mistake in C that D proliverates:
>>
>> The T.min value of signed types.
>>
>> e.g.
>>
>> 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.
---
import std.stdio;

void main() {
	byte a = -128;
	auto b = -a;
	writeln(typeof(b).stringof);
	writeln(b);
}
----

2.064 output:
byte
-128

Is this a bug or is it already fixed in 2.065?
January 25, 2014
On 1/24/2014 3:25 PM, Brad Roberts wrote:
> None of this is new.  It comes up periodically and ends up in the typical place
> of many feature requests, unimplemented.  Create the type, share it, see who
> uses it and how much they gain from the benefits and if they're worth the
> costs.  Conjecture will only get you so far.

I'll chime in that yes, you can create your own integral type in D.

You can in C++, too, there is a SafeInt integral type:

  http://msdn.microsoft.com/en-us/library/dd570023.aspx

  http://safeint.codeplex.com/

It's been around since at least 2004. I haven't noticed it getting much attention or traction. Maybe it would in D, you can always give it a try.
January 25, 2014
On 1/24/14 4:25 AM, 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

s/language/standard library/

> --> 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.

The short answer is - nagonna happen. Proposals for new standard library types and artifacts are of course accepted and encouraged.


Andrei

January 25, 2014
On Friday, 24 January 2014 at 23:26:04 UTC, Brad Roberts wrote:
> On 1/24/14 6:08 AM, Dominikus Dittes Scherkl wrote:
>> 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
> The only reason that NaN works in the FP world is that it's done in hardware as part of existing operations.

Walter wrote:
"There's no NaN for integrals."

At least the carry-bit is already available in hardware. So the save type doesn't incure much performance loss in operations. On the other hand comparison, assignment and casts become slower by a factor 2 or 3. And then comparison cannot be implemented fully correct with the current operator overloding system of D.

> It's, pretty much by definition, too expensive for a
> set of people and the apps they care about.
Yeah, but I think it's a small share and they could use the unsafe types - like they use manual memory management insted of GC.

> The only practical way is to introduce a new type that behaves like this and use that instead.
Thats what I did. I will share my code soon.
January 25, 2014
On 01/25/2014 01:57 PM, Dominikus Dittes Scherkl wrote:
> ...
> Walter wrote:
> "There's no NaN for integrals."
>
> At least the carry-bit is already available in hardware. So the save
> type doesn't incure much performance loss in operations. On the other
> hand comparison, assignment and casts become slower by a factor 2 or 3.
> And then comparison cannot be implemented fully correct with the current
> operator overloding system of D.

Why not?

struct S{
    auto opCmp(S r){ return float.nan; }
}

void main(){
    S s;
    assert(s!<>=s);
}

January 25, 2014
On Friday, 24 January 2014 at 22:59:08 UTC, Andrei Alexandrescu wrote:
> integral expressions are handled. For example I found it ridiculous that unary "-" for uint returns uint. Walter talked

Fortunately most CPUS have ones-complement so the result is correct if you only use one unsigned type.

-$01 == (~$01)+1
-$01 == $ff
 $ff+$03 == ($102)&($ff)
 $ff+$03 == $02

This is useful for interpolating oscillators (going from 1 to 0, or 0 to 1)

ulong phase, delta;
...
phase += delta;
sample1 = wavetable[phase>>16];
sample2 = wavetable[(phase>>16) + 1];
return interpolate16bit(sample1,sample2,phase&0xffff);

It is the automatic promoting of unsigned types that is a C-language bug IMHO.