www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Casting up to ints...

reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
One thing I've never liked, or completely understood, was the way 
arithmetic casts up to ints - but no higher.  Don't get me wrong, I 
don't at all mind it going from a short to an int... but why can't it go 
farther?

If I add two ubytes into a ulong, I get the correct result.

If I add two uints into a ulong, I get an incorrect result.

I realize this is because the + operator yields an int/uint result 
unless one of the values is a long/ulong.  But, since this doesn't 
happen for bytes and shorts, it seems terribly inconsistent.

And when you usually deal in small numbers, you get used to numeric 
overflows not happening without having to cast.  Then you deal with some 
large numbers, and create a bug.  This is why I think consistency in 
programming is so important.

So, all I'm asking is... why?  I mean, what is the reason for this 
behavior?  Is it just because, that's how C did it?  Or is there a 
greater reason I'm missing?

Otherwise it just seems like a case where we have to use the blunt 
instrument that is casting to make the compiler do something which is 
completely logical to humans.

Thanks,
-[Unknown]

PS: For those of you who need code to understand my question:

import std.stdio;

int main()
{
	ulong l1, l2;

	ubyte b1 = ubyte.max, b2 = ubyte.max;
	uint i1 = uint.max, i2 = uint.max;

	l1 = b1 + b2;
	l2 = i1 + i2;

	// l1 will be "right", l2 will be "wrong".
	writefln("Without casting:");
	writefln("ubyte + ubyte = %d\n\t(%d * 2)", l1, ubyte.max);
	writefln("uint + uint = %d\n\t(%d * 2)", l2, uint.max);

	l1 = cast(ulong) b1 + cast(ulong) b2;
	l2 = cast(ulong) i1 + cast(ulong) i2;

	// l1 and l2 will both be "right".
	writefln("With casting:");
	writefln("ubyte + ubyte = %d\n\t(%d * 2)", l1, ubyte.max);
	writefln("uint + uint = %d\n\t(%d * 2)", l2, uint.max);

	return 0;
}
Apr 29 2006
parent reply Don Clugston <dac nospam.com.au> writes:
Unknown W. Brackets wrote:
 One thing I've never liked, or completely understood, was the way 
 arithmetic casts up to ints - but no higher.  Don't get me wrong, I 
 don't at all mind it going from a short to an int... but why can't it go 
 farther?
 
 If I add two ubytes into a ulong, I get the correct result.
 
 If I add two uints into a ulong, I get an incorrect result.
 
 I realize this is because the + operator yields an int/uint result 
 unless one of the values is a long/ulong.  But, since this doesn't 
 happen for bytes and shorts, it seems terribly inconsistent.
 
 And when you usually deal in small numbers, you get used to numeric 
 overflows not happening without having to cast.  Then you deal with some 
 large numbers, and create a bug.  This is why I think consistency in 
 programming is so important.
 
 So, all I'm asking is... why?  I mean, what is the reason for this 
 behavior?  Is it just because, that's how C did it?  Or is there a 
 greater reason I'm missing?

There are very strong practical reasons. Basically it's because D targets 32-bit CPUs, and there is no performance cost to converting everything to 'int'. But converting everything to 'long' would be a massive performance hit on a 32-bit CPU. Even the x86-64 uses 32 bit arithmetic most of the time. It's extremely rare to find situations where ints overflow. int.max is an enormous number. This is emphatically not the case for short.max and byte.max.
 
 Otherwise it just seems like a case where we have to use the blunt 
 instrument that is casting to make the compiler do something which is 
 completely logical to humans.
 
 Thanks,
 -[Unknown]
 
 PS: For those of you who need code to understand my question:
 
 import std.stdio;
 
 int main()
 {
     ulong l1, l2;
 
     ubyte b1 = ubyte.max, b2 = ubyte.max;
     uint i1 = uint.max, i2 = uint.max;
 
     l1 = b1 + b2;
     l2 = i1 + i2;
 
     // l1 will be "right", l2 will be "wrong".
     writefln("Without casting:");
     writefln("ubyte + ubyte = %d\n\t(%d * 2)", l1, ubyte.max);
     writefln("uint + uint = %d\n\t(%d * 2)", l2, uint.max);
 
     l1 = cast(ulong) b1 + cast(ulong) b2;
     l2 = cast(ulong) i1 + cast(ulong) i2;
 
     // l1 and l2 will both be "right".
     writefln("With casting:");
     writefln("ubyte + ubyte = %d\n\t(%d * 2)", l1, ubyte.max);
     writefln("uint + uint = %d\n\t(%d * 2)", l2, uint.max);
 
     return 0;
 }

May 02 2006
parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
No, I realize that.  I definitely did not mean making everything 64 bit.

What I did mean was, it seems like this is rather detectable:

ulong a = cast(uint) b + cast(uint) c;

There are, of course, other areas where it is less detectable - such as 
function calls and template parameters - but in that case you still have 
a destination type to worry about.

I suppose, then, the worry would just be making the implicit conversion 
rules to complex.

-[Unknown]


 Unknown W. Brackets wrote:
 One thing I've never liked, or completely understood, was the way 
 arithmetic casts up to ints - but no higher.  Don't get me wrong, I 
 don't at all mind it going from a short to an int... but why can't it 
 go farther?

 If I add two ubytes into a ulong, I get the correct result.

 If I add two uints into a ulong, I get an incorrect result.

 I realize this is because the + operator yields an int/uint result 
 unless one of the values is a long/ulong.  But, since this doesn't 
 happen for bytes and shorts, it seems terribly inconsistent.

 And when you usually deal in small numbers, you get used to numeric 
 overflows not happening without having to cast.  Then you deal with 
 some large numbers, and create a bug.  This is why I think consistency 
 in programming is so important.

 So, all I'm asking is... why?  I mean, what is the reason for this 
 behavior?  Is it just because, that's how C did it?  Or is there a 
 greater reason I'm missing?

There are very strong practical reasons. Basically it's because D targets 32-bit CPUs, and there is no performance cost to converting everything to 'int'. But converting everything to 'long' would be a massive performance hit on a 32-bit CPU. Even the x86-64 uses 32 bit arithmetic most of the time. It's extremely rare to find situations where ints overflow. int.max is an enormous number. This is emphatically not the case for short.max and byte.max.

May 02 2006