March 29, 2021
On 3/29/2021 2:05 PM, Paul Backus wrote:
> On Monday, 29 March 2021 at 20:00:03 UTC, Walter Bright wrote:
>> D's integral promotion rules (bytes and shorts are promoted to ints before doing arithmetic) get rid of the bulk of likely overflows. (It's ironic that the integral promotion rules are much maligned and considered a mistake, I don't share that opinion, and this is one of the reasons why.)
> 
> Well...sometimes they do:
> 
>     auto result = int.max + int.max;
>     writeln(typeof(result).stringof); // int
>     writeln(result); // -2

I wrote "the bulk of", not "all"


> The main issue with D's integer promotion rules is that they're inconsistent. Sometimes truncating the result of an expression requires an explicit cast, and sometimes it doesn't.

Without an example, I don't know what you mean.
March 29, 2021
On 3/29/2021 3:47 PM, tsbockman wrote:
> On Monday, 29 March 2021 at 20:00:03 UTC, Walter Bright wrote:
>> It isn't even clear what the behavior on overflows should be. Error? Wraparound? Saturation?
> 
> It only seems unclear because you have accepted the idea that computer code "integer" operations may differ from mathematical integer operations in arbitrary ways.

Programmers need to accept that computer math is different in arbitrary ways. Not accepting it means a lifetime of frustration, because it cannot be the same.

> Otherwise, the algorithm is simple:
> 
>     if(floor(mathResult) <= codeResult && codeResult <= ceil(mathResult))
>         return codeResult;
>     else
>         signalErrorSomehow();

Some of the SIMD arithmetic instructions use saturation arithmetic. It is definitely a thing, and Intel found it profitable to add hardware support for it.


> Standard mathematical integer addition does not wrap around or saturate. When someone really wants an operation that wraps around or saturates (not just for speed's sake), then that is a different operation and should use a different name and/or type(s), to avoid sowing confusion and ambiguity throughout the codebase for readers and compilers.

That's what std.experimental.checkedint does.


> All of the integer behavior that people complain about violates this in some way: wrapping overflow, incorrect signed-unsigned comparisons, confusing/inconsistent implicit conversion rules,

The integral promotion rules have been standard practice for 40 years. It takes two sentences to describe them accurately. Having code that looks like C but behaves differently will be *worse*.


> undefined behavior of various more obscure operations for certain inputs, etc.

Offhand, I can't think of any.


> Mathematical integers are a more familiar, simpler, easier to reason about abstraction. When we use this abstraction, we can draw upon our understanding and intuition from our school days, use common mathematical laws and formulas with confidence, etc. Of course the behavior of the computer cannot fully match this infinite abstraction, but it could at least tell us when it is unable to do what was asked of it, instead of just silently doing something else.

These things all come at a cost. The cost is higher than the benefit.

Having D generate overflow checks on all adds and multiples will immediately make D uncompetitive with C, C++, Rust, Zig, Nim, etc.
March 29, 2021
On 3/29/2021 5:02 PM, H. S. Teoh wrote:
> Like I said, the only real flaw here is the choice of the name `int` for
> a hardware type that's clearly NOT an unbounded mathemetical integer.

You're right. It's not an integer, it's an int :-)

Besides, nobody is going to want to type `intMod32bit` every time they declare a variable. Heck, Rust chose `int32`, but that is of value only in the first 30 seconds of learning Rust, and will be cursed forever after.
March 29, 2021
On 3/29/21 3:25 PM, tsbockman wrote:
> On Monday, 29 March 2021 at 16:41:12 UTC, Andrei Alexandrescu wrote:
>> Checked code is larger, meaning more pressure on the scarce
>> I-cache in large programs - and that's not going to be visible
>> in microbenchmarks.
> 
> This is true. But, at the moment I don't have an easy way to quantify the size of that effect.

You actually do. Apply the scientific method.

This is not a new idea, most definitely has been around for years and people have tried a variety of things. So all you need to do is search around scholar.google.com for papers on the topic and plain google.com for other work on the topic. In a couple of minutes I found:

* https://dl.acm.org/doi/abs/10.1145/2743019 - relatively recent, quotes a lot of other work. A good starting point.

* -ftrapv and -fwrapv flags in gcc: https://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Code-Gen-Options.html. This is not quite what you're looking for (they just crash the program on overflow), but it's good to figure how much demand there is and how people use those flags.

* How popular is automated/manual overflow check in systems languages? Rust is a stickler for safety and it has explicit operations that check: https://stackoverflow.com/questions/52646755/checking-for-integer-overflow-in-rust. I couldn't find any proposal for C or C++. What does this lack of evidence suggest? etc.
March 30, 2021
On Tuesday, 30 March 2021 at 00:02:54 UTC, H. S. Teoh wrote:
> If you want mathematical integers, you should be using std.bigint or something similar instead.
>
>
>> Otherwise, the algorithm is simple:
>> 
>>     if(floor(mathResult) <= codeResult && codeResult <= ceil(mathResult))
>>         return codeResult;
>>     else
>>         signalErrorSomehow();
>
> Implementing such a scheme would introduce so much overhead that it would render the `int` type essentially useless for systems programming. Or for any application where performance is important, for that matter.

You have a wildly exaggerated sense of the runtime performance cost of doing things the way I advocate if you think it is anywhere close to bigint.

My proposal (grossly oversimplified) is mostly just to check the built-in CPU overflow flags once in a while. I've actually tested this, and even with a library solution the overhead is low in most realistic scenarios, if the inliner and optimizer are effective. A language solution could do even better, I'm sure.
March 30, 2021
On Tuesday, 30 March 2021 at 00:33:13 UTC, Walter Bright wrote:
> Having D generate overflow checks on all adds and multiples will immediately make D uncompetitive with C, C++, Rust, Zig, Nim, etc.

As someone else shared earlier in this thread, Zig already handles this in pretty much exactly the way I argue for:
    https://ziglang.org/documentation/master/#Integer-Overflow
March 29, 2021
On 3/29/2021 6:29 PM, tsbockman wrote:
> On Tuesday, 30 March 2021 at 00:33:13 UTC, Walter Bright wrote:
>> Having D generate overflow checks on all adds and multiples will immediately make D uncompetitive with C, C++, Rust, Zig, Nim, etc.
> 
> As someone else shared earlier in this thread, Zig already handles this in pretty much exactly the way I argue for:
>     https://ziglang.org/documentation/master/#Integer-Overflow

I amend my statement to "immediately make D as uncompetitive as Zig is"

Note that Zig has a very different idea of integers than D does. It has arbitrary bit width integers, up to 65535. This seems odd, as what are you going to do with a 6 bit integer? There aren't machine instructions to support it. It'd be better off with a ranged integer, say:

   i : int 0..64
March 30, 2021
On Tuesday, 30 March 2021 at 00:02:54 UTC, H. S. Teoh wrote:

> Just as when you use `float` or `double` you already signed up for IEEE semantics, like it or not. (I don't, but I also recognize that it's unrealistic to expect the hardware type to match up 100% with the mathematical ideal.) If you don't like that, use one of the real arithmetic libraries out there that let you work with "true" mathematical reals that aren't subject to the quirks of IEEE floating-point numbers. Just don't expect anything that will be competitive performance-wise.
>

I seems you are arguing against the way D broke compile time floats and doubles. )
March 29, 2021
On 3/29/2021 10:53 PM, Max Samukha wrote:
> On Tuesday, 30 March 2021 at 00:02:54 UTC, H. S. Teoh wrote:
> 
>> Just as when you use `float` or `double` you already signed up for IEEE semantics, like it or not. (I don't, but I also recognize that it's unrealistic to expect the hardware type to match up 100% with the mathematical ideal.) If you don't like that, use one of the real arithmetic libraries out there that let you work with "true" mathematical reals that aren't subject to the quirks of IEEE floating-point numbers. Just don't expect anything that will be competitive performance-wise.
>>
> 
> I seems you are arguing against the way D broke compile time floats and doubles. )

Compile-time isn't a run-time performance issue.
March 30, 2021
On Tuesday, 30 March 2021 at 06:43:04 UTC, Walter Bright wrote:
> On 3/29/2021 10:53 PM, Max Samukha wrote:
>> On Tuesday, 30 March 2021 at 00:02:54 UTC, H. S. Teoh wrote:
>> 
>>> Just as when you use `float` or `double` you already signed up for IEEE semantics, like it or not. (I don't, but I also recognize that it's unrealistic to expect the hardware type to match up 100% with the mathematical ideal.) If you don't like that, use one of the real arithmetic libraries out there that let you work with "true" mathematical reals that aren't subject to the quirks of IEEE floating-point numbers. Just don't expect anything that will be competitive performance-wise.
>>>
>> 
>> I seems you are arguing against the way D broke compile time floats and doubles. )
>
> Compile-time isn't a run-time performance issue.

On the subject of run-time performance, checkedint can also do things like Saturation arithmetic, which can be accelerated using increasingly common native instructions (e.g. AVX on Intel, AMD, and presumably Via also). I have done some tests and found that these are not currently used. ARM also has saturating instructions but I haven't done any tests.

Due to AVX being a SIMD instruction set there is a tradeoff to using them for scalar operations, however for loops the proposition seems attractive. The calculus to do this seems non-trivial for the backend however.

(AVX instructions are also quite big so there is a the usual I$ hit here too).