November 25, 2014
On Monday, 24 November 2014 at 19:06:35 UTC, Matthias Bentrup wrote:
> Agreed, though I don't like the explosion of new operators. I'd
> prefer the C# syntax like check(<expression>), wrap(expression),
> saturate(expression).

You maybe like this:
-------------------small test 1--------------------------
import std.stdio;

template  subuint(T1,T2){
auto subuint(T1 x, T2 y, ref bool overflow)
{
if(is(T1 == uint) && is(T2==uint))
	{
		if (x < y)
		{
        	return cast(int)(x -y);
		}
    	else
    	{
    		return x - y;
    	}
	}
	else if(is(T1 == uint) && is(T2==int))
	{writeln("enter here1");

		if (x < y)
		{ writeln("enter here2");
			return cast(int)(x -y);
		}
		else
		{ writeln("enter here3");
			return x - y;
		}
	}
	else if(is(T1 == int) && is(T2==uint))
	{
		if (x < y)
		{
        	return cast(int)(x -y);
		}
    	else
    	{
    		return x - y;
    	}
	}
	else if(is(T1 == int) && is(T2==int))
	{
		return x - y;
	}
  }
}

unittest
{
    bool overflow;
    assert(subuint(3, 2, overflow) == 1);
   assert(!overflow);
    assert(subuint(3, 4, overflow) == -1);

    assert(!overflow);
    assert(subuint(uint.max, 1, overflow) == uint.max - 1);
    writeln("typeid = ",typeid(subuint(uint.max, 1, overflow)));
    assert(!overflow);
    assert(subuint(1, 1, overflow) == uint.min);
    assert(!overflow);
    assert(subuint(0, 1, overflow) == -1);
    assert(!overflow);
    assert(subuint(uint.max - 1, uint.max, overflow) == -1);
    assert(!overflow);
    assert(subuint(0, 0, overflow) == 0);
    assert(!overflow);

	assert(subuint(3, -2, overflow) == 5);
    assert(!overflow);
    assert(subuint(uint.max, -1, overflow) == uint.max + 1);

    assert(!overflow);
    assert(subuint(1, -1, overflow) == 2);
    assert(!overflow);
    assert(subuint(0, -1, overflow) == 1);
    assert(!overflow);
    assert(subuint(uint.max - 1, int.max, overflow) == int.max);
    assert(!overflow);
    assert(subuint(0, 0, overflow) == 0);
    assert(!overflow);
    assert(subuint(-2, 1, overflow) == -3);
    assert(!overflow);
}


void main()
{
	 uint a= 3;
	 int b = 4;
	 int c =2;
	  writeln("c -a =",c-a);
	 writeln("a -b =",a-b);
	  writeln("----------------");
	bool overflow;
	 writeln("typeid = ",typeid(subuint(a, b, overflow)),", a-b=",subuint(a, b, overflow));
	writeln("ok");
}

---------------here is a simple ,but it's error--------------------------
import std.stdio;

template  subuint(T1,T2){
auto subuint(T1 x, T2 y, ref bool overflow)
{
	if(is(T1 == int) && is(T2==int))
	{
		return x - y;
	}
	else if((is(T1 == uint) && is(T2==int)) | (is(T1 == uint) && is(T2==uint)) | (is(T1 == int) && is(T2==uint)))
	{
		if (x < y)
		{
        	return cast(int)(x -y);
		}
    	else
    	{
    		return x - y;
    	}
	}
  }
}


void main()
{
	 uint a= 3;
	 int b = 4;
	 int c =2;
	  writeln("c -a =",c-a);
	 writeln("a -b =",a-b);
	  writeln("----------------");
	bool overflow;
	 writeln("typeid = ",typeid(subuint(a, b, overflow)),", a-b=",subuint(a, b, overflow));
	writeln("ok");
}

November 25, 2014
On Monday, 24 November 2014 at 16:00:53 UTC, ketmar via Digitalmars-d wrote:
> On Mon, 24 Nov 2014 12:54:58 +0000
> Don via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>> In D,  1u - 2u > 0u. This is defined behaviour, not an overflow.
> this *is* overflow. D just has overflow result defined.

No, that is not overflow. That is a carry. Overflow is when the sign bit changes.
November 25, 2014
On Monday, 24 November 2014 at 21:34:19 UTC, Walter Bright wrote:
> On 11/24/2014 2:20 AM, Don wrote:
>>> I believe I do understand the problem. As a practical matter, overflow checks
>>> are not going to be added for performance reasons.
>>
>> The performance overhead would be practically zero. All we would need to do, is
>> restrict array slices such that the length cannot exceed ssize_t.max.
>>
>> This can only happen in the case where the element type has a size of 1, and
>> only in the case of slicing a pointer, concatenation, and memory allocation.
>
> (length1 + length2) / 2

That's not an issue with length, that's an issue with doing a calculation with an insufficient bit width. Unsigned doesn't actually help, it's still wrong.

For unsigned values, if length1 = length2 = 0x8000_0000, that gives an answer of 0.


>> In exchange, 99% of uses of unsigned would disappear from D code, and with it, a
>> whole category of bugs.
>
> You're not proposing changing size_t, so I believe this statement is incorrect.

From the D code that I've seen, almost all uses of size_t come directly from the use of .length. But I concede (see below) that many of them come from .sizeof.

>>> Also, in principle, uint-uint can generate a runtime check for underflow (i.e.
>>> the carry flag).
>>
>> No it cannot. The compiler does not have enough information to know if the value
>> is intended to be positive integer, or an unsigned. That information is lost
>> from the type system.
>>
>> Eg from C, wrapping of an unsigned type is not an error. It is perfectly defined
>> behaviour. With signed types, it's undefined behaviour.
>
> I know it's not an error. It can be defined to be an error, and the compiler can insert a runtime check. (I'm not proposing this, just saying it can be done.)

But it can't do that, without turning unsigned into a different type.
You'd be turning unsigned into a 'non-negative' which is a completely different type. This is my whole point.

unsigned has no sign, you just get the raw bit pattern with no interpretation.
This can mean several things, for example:
1. extended_non_negative is where you are using it for the positive range 0.. +0xFFFF_FFFF
  Then, overflow and underflow are errors.
2. a value where the highest bit is always 0. This can be safely used as int or uint.
3. Or, it can be modulo 2^^32 arithmetic, where wrapping is intended.
4. It can be part of extended precision arithmetic, where you want the carry flag.
5. It can be just a raw bit pattern.
6. The high bit can be a sign bit. This is a signed type, cast to uint.
If the sign bit ever flips because of a carry, that's an error.

The type system doesn't specify a meaning for the bit pattern. We've got a special type for case 6, but not for the others.

The problem with unsigned is that since it can mean so many things, as if it were a union of these possibilities. So it's not strictly typed -- you need to careful, requiring some element of faith-based programming.

And "signed-unsigned mismatch" is really where you are implicitly assuming that the unsigned value is case 2 or 6.  But, if it is one of the other cases, you get nonsense.

But those "signed unsigned mismatch" errors only catch some of the possible cases where you may forget which interpretation you are using, and act as if it were another one.


>> To make this clear: I am not proposing that size_t should be changed.
>> I am proposing that for .length returns a signed type, that for array slices is
>> guaranteed to never be negative.
>
> There'll be mass confusion if .length is not the same type as .sizeof

Ah, that is a good point. .sizeof is another source of unsigned.
Again, quite unnecessarily, can a single type ever actually use up half of the memory space? (It was possible in the 8 and 16 bit days, but it's hard to imagine today). Even sillier, it is nearly always known at compile time!

But still, .sizeof is low-level in a way that .length is not.
November 25, 2014
On Tuesday, 25 November 2014 at 07:39:44 UTC, Don wrote:
> No, that is not overflow. That is a carry. Overflow is when the sign bit changes.

I think this discussion will be less confusing with clearing up the terminology.

An overflow condition happens when the representation cannot hold the magnitude of the intended type. In floating point that is +Inf and -Inf.

And underflow condition happens when the representation cannot represent the precision of small numbers. In floating point that is +0, -0 and denormal numbers, detected or undetected.

Carry is an extra bit that can be considered part of the computation for a concrete machine code instruction that provides carry. Eg 32bits + 32bits => (32+1) bits.

If the intended type is true Reals and the representation is integer then we get:

0u - 1u => overflow
1u / 2u => underflow

Carry can be taken as an overflow condition, but it is not proper overflow if you interpret it as s part of the result that depends on the machine language instruction and use of it. For a regular ADD/SUB instruction with carry the ALU covers two intended types (signed/unsigned) and use the control register flags in a way which let's the programmer make the interpretation.

Some SIMD instructions does not provide control register flags and are therefore true modular arithmetic that does not overflow by definition, but if you use them for representing a non-modular intended type then you get undetected overflow…

Overflow is in relation to an interpretation: the intended type versus the internal representation and the concrete machine language instruction.
November 25, 2014
On Monday, 24 November 2014 at 21:34:19 UTC, Walter Bright wrote:
>> In exchange, 99% of uses of unsigned would disappear from D code, and with it, a
>> whole category of bugs.
>
> You're not proposing changing size_t, so I believe this statement is incorrect.

The idea is to make unsigned types opt-in, a deliberate choice of individual programmers, not forced by the language. Positive signed integers convert to unsigned integers perfectly without losing information, so mixing types will work perfectly for those who request it.
November 25, 2014
On Monday, 24 November 2014 at 15:56:44 UTC, Andrei Alexandrescu wrote:
> On 11/24/14 4:54 AM, Don wrote:
>> In D,  1u - 2u > 0u. This is defined behaviour, not an overflow.
>
> I think I get what you mean, but overflow is also defined behavior (in D at least). -- Andrei


Aargh! You're right. That's new, and dreadful. It didn't used to be.
The offending commit is

alexrp              2012-05-15 15:37:24

which only provides an unsigned example.

Why are defining behaviour that is always a bug? Java makes it defined, but it has to because it doesn't have unsigned types.
I think the intention probably was to improve on the C situation, where there is undefined behaviour that really should be defined.

But do we really want to preclude ever having overflow checking for integers?

November 25, 2014
Don:

> Aargh! You're right. That's new, and dreadful. It didn't used to be.
> The offending commit is
>
> alexrp              2012-05-15 15:37:24
>
> which only provides an unsigned example.
>
> Why are defining behaviour that is always a bug? Java makes it defined, but it has to because it doesn't have unsigned types.
> I think the intention probably was to improve on the C situation, where there is undefined behaviour that really should be defined.
>
> But do we really want to preclude ever having overflow checking for integers?

+1

Bye,
bearophile
November 25, 2014
On Tuesday, 25 November 2014 at 11:43:01 UTC, Don wrote:
> Why are defining behaviour that is always a bug? Java makes it defined, but it has to because it doesn't have unsigned types.
> I think the intention probably was to improve on the C situation, where there is undefined behaviour that really should be defined.

Mostly to prevent optimizations based on no-overflow assumption.

> But do we really want to preclude ever having overflow checking for integers?

Overflow checking doesn't contradict to overflow being defined. The latter simply reflects how hardware works, nothing else. And hardware works that way, because that's a fast implementation of arithmetic for general case.
November 25, 2014
On Tuesday, 25 November 2014 at 13:52:32 UTC, Kagamin wrote:
> Overflow checking doesn't contradict to overflow being defined. The latter simply reflects how hardware works, nothing else. And hardware works that way, because that's a fast implementation of arithmetic for general case.

So you are basically saying that D does not provide modular arithmetic, but allows you to continue with the incorrect result of an overflow as a modulo representation?

Because you have to choose, you cannot both have modular arithmetic and overflow at the same time for the same operator. Overflow happens because you have monotonic semantics for addition, not modular semantics.

Btw,  http://dlang.org/expression needs a clean up, the term "underflow" is not used correctly.
November 25, 2014
On Tuesday, 25 November 2014 at 14:30:36 UTC, Ola Fosheim Grøstad wrote:
> So you are basically saying that D does not provide modular arithmetic, but allows you to continue with the incorrect result of an overflow as a modulo representation?

Correctness is an emergent property - when behavior matches expectation, so overflow has variable correctness in various parts of the code.