June 11, 2013
On Tue, 11 Jun 2013 07:46:11 -0400, Temtaime <temtaime@gmail.com> wrote:

> No. I means, that
>
> uint a = uint.max;
> uint b = a + 1;
> writeln(b);
>
> Works OK.
> Why? Compiler doesn't know if a + b fits in uint, right?
> Then why overflow with ints are accepted?

CPU performs math at int level.  So even if you add two ubytes, they are added at integer level.

For instance, this is valid, and does not produce a truncated uint:

ubyte x = ubyte.max;
uint y = x + x;
assert(y == 255 + 255);

Essentially the compiler is making sure you wanted to throw away that extra precision that it had to create anyways because that's what the CPU supports.

It can take some getting used to.

Note that += does not have this problem:

ubyte c = k;
c += 1; // ok

This I find extremely inconsistent...

-Steve
June 11, 2013
On Tuesday, 11 June 2013 at 16:05:30 UTC, Steven Schveighoffer wrote:
> CPU performs math at int level.

eh, I wouldn't blame the hardware. You can do

asm {
  mov AL, 10;
  add AL, 5;
}

and it is allowed, it also don't spill into AH if you overflow it (it just sets the carry flag). I'm sure it is different on different processors, but x86 is pretty flexible.


> ubyte c = k;
> c += 1; // ok
>
> This I find extremely inconsistent...

I'd be extremely annoyed if that required a cast. It's bleeding obvious that you want it to assign back there....
June 11, 2013
On Tuesday, 11 June 2013 at 16:18:54 UTC, Adam D. Ruppe wrote:
> I'd be extremely annoyed if that required a cast. It's bleeding obvious that you want it to assign back there....

To me u = u + k is as obvious as u += k, but that's probably not a thing anyone would be much concerned about :)
June 11, 2013
On Tue, 11 Jun 2013 12:18:52 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:

> On Tuesday, 11 June 2013 at 16:05:30 UTC, Steven Schveighoffer wrote:
>> CPU performs math at int level.
>
> eh, I wouldn't blame the hardware. You can do
>
> asm {
>    mov AL, 10;
>    add AL, 5;
> }
>
> and it is allowed, it also don't spill into AH if you overflow it (it just sets the carry flag). I'm sure it is different on different processors, but x86 is pretty flexible.

Well, that is not what I knew, so good lesson :)

But I'd be surprised if the above was less costly than a full word addition.  I think that is the point of C's policy of integer promotion, which D adopts for the most part (although C allows it to go back into whatever).

>
>
>> ubyte c = k;
>> c += 1; // ok
>>
>> This I find extremely inconsistent...
>
> I'd be extremely annoyed if that required a cast. It's bleeding obvious that you want it to assign back there....

I argue the consistency is both ways :)  I don't want to require a cast there (and in reality, where would the cast go?), but it seems that if you are doing math among bytes, or a byte and an integer that can fit into a byte, it should be quite obvious that you are working in the byte land.  The fact that

k += 1;

and

k = k + 1;

are treated differently is annoying.

-Steve
June 11, 2013
On Tue, Jun 11, 2013 at 01:25:24PM -0400, Steven Schveighoffer wrote:
> On Tue, 11 Jun 2013 12:18:52 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:
> 
> >On Tuesday, 11 June 2013 at 16:05:30 UTC, Steven Schveighoffer wrote:
> >>CPU performs math at int level.
> >
> >eh, I wouldn't blame the hardware. You can do
> >
> >asm {
> >   mov AL, 10;
> >   add AL, 5;
> >}
> >
> >and it is allowed, it also don't spill into AH if you overflow it (it just sets the carry flag). I'm sure it is different on different processors, but x86 is pretty flexible.
> 
> Well, that is not what I knew, so good lesson :)
> 
> But I'd be surprised if the above was less costly than a full word addition.  I think that is the point of C's policy of integer promotion, which D adopts for the most part (although C allows it to go back into whatever).

It may or may not be less costly as full-word addition, but it definitely allows the optimizer more wiggle room, because it allows more byte-sized values to fit into registers before spilling over to memory accesses.

But that doesn't really pertain to this discussion. :-P


> >>ubyte c = k;
> >>c += 1; // ok
> >>
> >>This I find extremely inconsistent...
> >
> >I'd be extremely annoyed if that required a cast. It's bleeding obvious that you want it to assign back there....
> 
> I argue the consistency is both ways :)  I don't want to require a cast there (and in reality, where would the cast go?), but it seems that if you are doing math among bytes, or a byte and an integer that can fit into a byte, it should be quite obvious that you are working in the byte land.  The fact that
> 
> k += 1;
> 
> and
> 
> k = k + 1;
> 
> are treated differently is annoying.

Yeah, I agree that is annoyingly inconsistent. I'm sure there are good reasons for it -- avoid common overflow bugs, for example, while not making regular int arithmetic a pain to work with by requiring casts everywhere. But still, it *is* an inconsistency, and it's still annoying.


T

-- 
Ruby is essentially Perl minus Wall.
June 11, 2013
On Tue, Jun 11, 2013 at 11:26 AM, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:

> On Tue, Jun 11, 2013 at 01:25:24PM -0400, Steven Schveighoffer wrote:
> > On Tue, 11 Jun 2013 12:18:52 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:
> >
> > >On Tuesday, 11 June 2013 at 16:05:30 UTC, Steven Schveighoffer wrote:
> > >>CPU performs math at int level.
> > >
> > >eh, I wouldn't blame the hardware. You can do
> > >
> > >asm {
> > >   mov AL, 10;
> > >   add AL, 5;
> > >}
> > >
> > >and it is allowed, it also don't spill into AH if you overflow it (it just sets the carry flag). I'm sure it is different on different processors, but x86 is pretty flexible.
> >
> > Well, that is not what I knew, so good lesson :)
> >
> > But I'd be surprised if the above was less costly than a full word addition.  I think that is the point of C's policy of integer promotion, which D adopts for the most part (although C allows it to go back into whatever).
>
> It may or may not be less costly as full-word addition, but it definitely allows the optimizer more wiggle room, because it allows more byte-sized values to fit into registers before spilling over to memory accesses.
>
> But that doesn't really pertain to this discussion. :-P
>
>
> > >>ubyte c = k;
> > >>c += 1; // ok
> > >>
> > >>This I find extremely inconsistent...
> > >
> > >I'd be extremely annoyed if that required a cast. It's bleeding obvious that you want it to assign back there....
> >
> > I argue the consistency is both ways :)  I don't want to require a cast there (and in reality, where would the cast go?), but it seems that if you are doing math among bytes, or a byte and an integer that can fit into a byte, it should be quite obvious that you are working in the byte land.  The fact that
> >
> > k += 1;
> >
> > and
> >
> > k = k + 1;
> >
> > are treated differently is annoying.
>
> Yeah, I agree that is annoyingly inconsistent. I'm sure there are good reasons for it -- avoid common overflow bugs, for example, while not making regular int arithmetic a pain to work with by requiring casts everywhere. But still, it *is* an inconsistency, and it's still annoying.
>
>
> T
>
> --
> Ruby is essentially Perl minus Wall.
>

wouldn't it be a better and more consistent idea to implement bearophile's 'Compiler support to implement efficient safe integrals' http://d.puremagic.com/issues/show_bug.cgi?id=9850 so uint/int etc would require no cast


June 11, 2013
On Tuesday, 11 June 2013 at 19:09:11 UTC, Timothee Cour wrote:
> wouldn't it be a better and more consistent idea to implement bearophile's 'Compiler support to implement efficient safe integrals'
> http://d.puremagic.com/issues/show_bug.cgi?id=9850 so uint/int etc would require no cast

This isn't really about overflow though, it is about cutting off bits on assignment.

int a = 0;
byte b = a;

test11.d(3): Error: cannot implicitly convert expression (a) of type int to byte

The only reason this comes up in the OP's case is because of the int promotion for arithmetic.
June 12, 2013
On 2013-06-11 07:35, Adam D. Ruppe wrote:
> On Tuesday, 11 June 2013 at 10:12:27 UTC, Temtaime wrote:
>> ubyte k = 10;
>>ubyte c = k + 1;
>>
>> This code fails to compile because of: Error: cannot implicitly
>> convert expression (cast(int)k + 1) of type int to ubyte
>
> The reason is arithmetic operations transform the operands into ints,
> that's why the error says cast(int)k. Then it thinks int is too big
> for ubyte. It really isn't about overflow, it is about truncation.
>
> That's why uint + 1 is fine. The result there is still 32 bits so
> assigning it to a 32 bit number is no problem, even if it does
> overflow. But k + 1 is promoted to int first, so it is a 32 bit
> number and now the compiler complains that you are trying to shove it
> into an 8 bit variable. Unless it can prove the result still fits in
> 8 bits, it complains, and it doesn't look outside the immediate line
> of code to try to prove it. So it thinks k can be 255, and 255 + 1 =
> 256, which doesn't fit in 8 bits.
>
> The promotion to int is something D inherited from C and probably
> isn't going anywhere.

i think part of the problem is that '1' is an int. so the calculation must be promoted to integer.
if we had byte and ubyte integer literals (suffix b/B and ub/UB?), then if all RHS arguments are (unsigned) bytes the compiler could infer that we are serious with sticking to bytes...

ubyte k = 10;	// or optional 10ub
ubyte c = k + 1ub;
June 12, 2013
On 06/11/2013 05:48 PM, captaindet wrote:

> i think part of the problem is that '1' is an int. so the calculation
> must be promoted to integer.

According to "Integer Promotions" and "Usual Arithmetic Conversions" there is no arithmetic operation that is executed in any type narrower than int:

  http://dlang.org/type.html

> if we had byte and ubyte integer literals (suffix b/B and ub/UB?), then
> if all RHS arguments are (unsigned) bytes the compiler could infer that
> we are serious with sticking to bytes...

The CPU would have to have special registers to do those operations in. (I am under the impression that x86 does not have any arithmetic registers for types narrower than int.)

> ubyte k = 10;    // or optional 10ub
> ubyte c = k + 1ub;

Things could be that way but I think these rules are inherited from C. Here is another surprising effect:

import std.stdio;

void foo(int)
{
    writeln("int");
}

void foo(byte)
{
    writeln("byte");
}

void main()
{
    byte a, b;

    foo(a + b);    // prints "int"
    foo(a);        // prints "byte"
}

Ali

June 12, 2013
On Wednesday, 12 June 2013 at 00:58:17 UTC, Ali Çehreli wrote:
> The CPU would have to have special registers to do those operations in. (I am under the impression that x86 does not have any arithmetic registers for types narrower than int.)

It does, for 8, 16, 32, and 64 bits. Here's how 8 and 16 bit arithmetic looks:

import std.stdio;

void main() {
	ubyte a;
	ubyte h;
	// byte addition and subtraction
	// demoing cast(ubyte) 255 + cast(ubyte) 10 - cast(ubyte) 5
	asm {
		mov AL, 255;
		add AL, 10;
		sub AL, 5;
		mov [a], AL;
		mov [h], AH;
	}
	writefln("AL: %d  AH: %d", a, h);

	// byte multiplication, answer goes into a 16 bit register, but we could truncate it too
	// demoing cast(ubyte) 50 * cast(ubyte) 10
	ushort response;
	asm {
		mov AL, 50;
		mov BL, 10;
		imul BL;
		mov [response], AX; // the full answer
		mov [a], AL; // explicit cast to ubyte
	}

	writeln("50 * 10 = ", response, " casted to ubyte gives ", a);

	// division works the other way
	// demoing cast(ubyte) 252 / cast(ubyte) 5
	ubyte quotient, remainder;
	asm {
		mov AH, 0; // clear the high byte because divide uses both, but we're explicitly talking bytes here so we can just zero it
		mov AL, 252; // our byte value
		mov BL, 5; // another byte
		idiv BL;
		mov [quotient], AL;
		mov [remainder], AH;
	}

	writeln("252 / 50 = ", quotient, " remainder ", remainder);



	// the x86 can do 16 bits as well

	ushort s1, s2;
	asm {
		mov AX, 65534;
		add AX, 4;
		mov [s1], AX;
	}
	writeln("16 bit add, expecting overflow: ", s1);

	asm {
		mov AX, 1000;
		mov BX, 1000;
		mul BX; // AX * BX

		// the answer is 32 bits here, just like 8 bit multiply put the answer in 16 bits
		mov [s1], DX; // the high word
		mov [s2], AX; // the low word
	}

	writeln("16 bit mul casted to ushort: ", s2, " full answer: ", s1 << 16 | s2);

	// you get the idea by now.....
}


32 bit and 64 bit follow the same pattern, with ever growing registers. This code runs with both -m32 and -m64 (and it would work as 16 bit if D had that option... dmc does if you actually want to try it!)