www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Integer promotion... what I'm missing? (It's monday...)

reply Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> writes:
Hi all,

What I'm missing?

     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16

Thanks

---
Paolo
Jun 19 2006
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Paolo Invernizzi wrote:
 Hi all,
 
 What I'm missing?
 
     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16
 
 Thanks
 
 ---
 Paolo

If you read http://digitalmars.com/d/type.html, you will find the rules for integer promotion under arithmetic operations (which I assume also includes comparisons). Note point 5.4 under "Usual Arithmetic Conversions": 5.4. The signed type is converted to the unsigned type. Hence, b is being converted to a uint. cast(uint)(-1) = ~0 = uint.max, hence why b is not less than a. If you're going to compare a signed and unsigned type directly, it's best to explicitly cast them to a common type, or strange things like this could happen :) Either convert a to an int (and watch for overflow), or convert them both to a long (so you don't have problems with overflow). -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Jun 19 2006
parent reply "Tony" <ignorethis nowhere.com> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:e75qg4$2bt$1 digitaldaemon.com...
 Paolo Invernizzi wrote:
 Hi all,

 What I'm missing?

     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16

 Thanks

 ---
 Paolo

If you read http://digitalmars.com/d/type.html, you will find the rules for integer promotion under arithmetic operations (which I assume also includes comparisons). Note point 5.4 under "Usual Arithmetic Conversions": 5.4. The signed type is converted to the unsigned type. Hence, b is being converted to a uint. cast(uint)(-1) = ~0 = uint.max, hence why b is not less than a. If you're going to compare a signed and unsigned type directly, it's best to explicitly cast them to a common type, or strange things like this could happen :) Either convert a to an int (and watch for overflow), or convert them both to a long (so you don't have problems with overflow). -- Daniel

I believe it is a mistake to have an operation like this occur when it is both mathematically incorrect and unintuitive. It feels like something that is destined to be a Bug Breeder. I would prefer that any type promotions (and any implicit operations for that matter) should guarantee not to introduce any data corruption. In this case, promote both numbers to a third type which can hold all possible values from both. If the user wishes to override that by making explicit conversions, at least this will be visible in the code. Tony Melbourne, Australia
Jun 19 2006
next sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Tony wrote:
 I would prefer that any type promotions (and any implicit operations for 
 that matter) should guarantee not to introduce any data corruption.  In this 
 case, promote both numbers to a third type which can hold all possible 
 values from both.

I agree! Perhaps this could also prevent the "array.length-1" bug (which is ~0 when array.length == 0), by making "array.length-1" a "long" instead of a "uint". L.
Jun 19 2006
prev sibling next sibling parent "Derek Parnell" <derek psych.ward> writes:
On Mon, 19 Jun 2006 20:11:32 +1000, Tony <ignorethis nowhere.com> wrote:


 If you read http://digitalmars.com/d/type.html, you will find the rules
 for integer promotion under arithmetic operations (which I assume also
 includes comparisons).

 Note point 5.4 under "Usual Arithmetic Conversions":

 5.4. The signed type is converted to the unsigned type.


 I believe it is a mistake to have an operation like this occur when it is
 both mathematically incorrect and unintuitive.  It feels like something  
 that is destined to be a Bug Breeder.

No argument from me. This has often caused me grief. The two most often gotchas for me is mismatched template parameters due to this stupid rule, and that array length is a uint which means that all expressions involving array.length get silently converted to uint before the evaluation and that can mess up index accesses. -- Derek Parnell Melbourne, Australia
Jun 19 2006
prev sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Tony wrote:
 I would prefer that any type promotions (and any implicit operations for 
 that matter) should guarantee not to introduce any data corruption.  In this 
 case, promote both numbers to a third type which can hold all possible 
 values from both.
 

What should be done when one number is an ulong too big to fit in a long and the other is a negative number? (Replace "long" with "cent" if we ever get the type.) No matter how the promotion is done, corruption occurs. I think a warning for all such cases would suffice --- I thought one would be emitted for the original code, but evidently not.
Jun 19 2006
parent reply Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> writes:
Deewiant wrote:
 Tony wrote:
 I would prefer that any type promotions (and any implicit operations for 
 that matter) should guarantee not to introduce any data corruption.  In this 
 case, promote both numbers to a third type which can hold all possible 
 values from both.

emitted for the original code, but evidently not.

Instead of a warning, I would prefer an error, forcing for an explicit cast if the comparison it's what you really want to to. But only for rule number 4. Cheers --- Paolo
Jun 19 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Paolo Invernizzi wrote:
 Instead of a warning, I would prefer an error, forcing for an explicit 
 cast if the comparison it's what you really want to to. But only for 
 rule number 4.

I'd also like DMD to issue an error in this case, as I've stumbled upon it a few times. -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 19 2006
prev sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 20 Jun 2006 04:06:37 +0200, Paolo Invernizzi wrote:

 Deewiant wrote:
 Tony wrote:
 I would prefer that any type promotions (and any implicit operations for 
 that matter) should guarantee not to introduce any data corruption.  In this 
 case, promote both numbers to a third type which can hold all possible 
 values from both.

emitted for the original code, but evidently not.

Instead of a warning, I would prefer an error, forcing for an explicit cast if the comparison it's what you really want to to. But only for rule number 4.

Just to note that with DMD, a 'warning' is really an optional error. In other words, if you have -w switch and it displays a 'warning', the compiler still fails to create the object file because it treats such warnings as if they were errors. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 20/06/2006 11:04:06 AM
Jun 19 2006
prev sibling parent reply Alexander Panek <alexander.panek brainsware.org> writes:
If you take a look at how comparison works, you'll know why this one fails.

Lets take an uint a = 16; as in your example:
00000000 00000000 00000000 00010000

And now a signed integer with the value -1:
10000000 00000000 00000000 00000001

You might guess which number is bigger, when our comparison is done 
binary (and after all, that's what the processor does) :)

Regards,
Alex

Paolo Invernizzi wrote:
 Hi all,
 
 What I'm missing?
 
     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16
 
 Thanks
 
 ---
 Paolo

Jun 27 2006
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Alexander Panek wrote:
 If you take a look at how comparison works, you'll know why this one fails.
 
 Lets take an uint a = 16; as in your example:
 00000000 00000000 00000000 00010000
 
 And now a signed integer with the value -1:
 10000000 00000000 00000000 00000001
 

Your point still stands, but -1 is represented as: 11111111 11111111 11111111 11111111 http://en.wikipedia.org/wiki/Two%27s_complement
 You might guess which number is bigger, when our comparison is done 
 binary (and after all, that's what the processor does) :)
 
 Regards,
 Alex
 
 Paolo Invernizzi wrote:
 
 Hi all,

 What I'm missing?

     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16

 Thanks

 ---
 Paolo


Jun 27 2006
next sibling parent Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> writes:
You both are true, and I know about Two's complement...

But, as  others in the thread pointed out, that's just a way of 
"represent" a number, and, worst, in this case, they are just "constant":

   uint a = 16;
   int b = -1;
   assert(b < a);

No ambiguity at all. This can be folded.

And in the real world, -1 is less than 16. So, as one of the main 
principle of the D programming language is to minimize the risks of 
bugs, I feel that at least a Warning should be raised for that code...

But really, it's just a minor glitch...

Cheers

---
Paolo


Kirk McDonald wrote:
 Alexander Panek wrote:
 If you take a look at how comparison works, you'll know why this one 
 fails.

 Lets take an uint a = 16; as in your example:
 00000000 00000000 00000000 00010000

 And now a signed integer with the value -1:
 10000000 00000000 00000000 00000001

Your point still stands, but -1 is represented as: 11111111 11111111 11111111 11111111 http://en.wikipedia.org/wiki/Two%27s_complement
 You might guess which number is bigger, when our comparison is done 
 binary (and after all, that's what the processor does) :)

 Regards,
 Alex

 Paolo Invernizzi wrote:

 Hi all,

 What I'm missing?

     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16

 Thanks

 ---
 Paolo



Jun 28 2006
prev sibling parent reply Max Samuha <maxter i.com.ua_spamless> writes:
On Tue, 27 Jun 2006 12:33:32 -0700, Kirk McDonald
<kirklin.mcdonald gmail.com> wrote:

Alexander Panek wrote:
 If you take a look at how comparison works, you'll know why this one fails.
 
 Lets take an uint a = 16; as in your example:
 00000000 00000000 00000000 00010000
 
 And now a signed integer with the value -1:
 10000000 00000000 00000000 00000001
 

Your point still stands, but -1 is represented as: 11111111 11111111 11111111 11111111 http://en.wikipedia.org/wiki/Two%27s_complement
 You might guess which number is bigger, when our comparison is done 
 binary (and after all, that's what the processor does) :)
 
 Regards,
 Alex
 
 Paolo Invernizzi wrote:
 
 Hi all,

 What I'm missing?

     uint a = 16;
     int b = -1;
     assert( b < a ); // this fails! I was expecting that -1 < 16

 Thanks

 ---
 Paolo



Maybe it's not a bug but it is very confusing, no matter how integer operations work internally. Compiler should give at least a warning about incompatibe types, or try to cast uint to int implicitly or require an explicit cast.
Jun 28 2006
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Max Samuha wrote:
 On Tue, 27 Jun 2006 12:33:32 -0700, Kirk McDonald
 <kirklin.mcdonald gmail.com> wrote:
 
 
Alexander Panek wrote:

If you take a look at how comparison works, you'll know why this one fails.

Lets take an uint a = 16; as in your example:
00000000 00000000 00000000 00010000

And now a signed integer with the value -1:
10000000 00000000 00000000 00000001

Your point still stands, but -1 is represented as: 11111111 11111111 11111111 11111111 http://en.wikipedia.org/wiki/Two%27s_complement
You might guess which number is bigger, when our comparison is done 
binary (and after all, that's what the processor does) :)

Regards,
Alex

Paolo Invernizzi wrote:


Hi all,

What I'm missing?

    uint a = 16;
    int b = -1;
    assert( b < a ); // this fails! I was expecting that -1 < 16

Thanks

---
Paolo



Maybe it's not a bug but it is very confusing, no matter how integer operations work internally. Compiler should give at least a warning about incompatibe types, or try to cast uint to int implicitly or require an explicit cast.

It's worth noting that this behavior (of a being less than b) follows the implicit conversion rules exactly: http://www.digitalmars.com/d/type.html [snipped non-applicable checks...] 5. Else the integer promotions are done on each operand, followed by: 1. If both are the same type, no more conversions are done. 2. If both are signed or both are unsigned, the smaller type is converted to the larger. 3. If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type. 4. The signed type is converted to the unsigned type. So the int is implicitly converted to the uint, and (apparently) it simply compares 2**32-1 to 16. So I wouldn't call this a bug, just a potential oddity. Maybe it should detect and throw an overflow if a negative signed integer is converted to an unsigned type? Or not: I'd consider this an edge case. It's probably considered bad practice to promiscuously mix signed and unsigned types. -Kirk McDonald
Jun 28 2006
parent reply Don Clugston <dac nospam.com.au> writes:
Kirk McDonald wrote:
 Max Samuha wrote:
 On Tue, 27 Jun 2006 12:33:32 -0700, Kirk McDonald
 <kirklin.mcdonald gmail.com> wrote:


 Alexander Panek wrote:

 If you take a look at how comparison works, you'll know why this one 
 fails.

 Lets take an uint a = 16; as in your example:
 00000000 00000000 00000000 00010000

 And now a signed integer with the value -1:
 10000000 00000000 00000000 00000001

Your point still stands, but -1 is represented as: 11111111 11111111 11111111 11111111 http://en.wikipedia.org/wiki/Two%27s_complement
 You might guess which number is bigger, when our comparison is done 
 binary (and after all, that's what the processor does) :)

 Regards,
 Alex

 Paolo Invernizzi wrote:


 Hi all,

 What I'm missing?

    uint a = 16;
    int b = -1;
    assert( b < a ); // this fails! I was expecting that -1 < 16

 Thanks

 ---
 Paolo



Maybe it's not a bug but it is very confusing, no matter how integer operations work internally. Compiler should give at least a warning about incompatibe types, or try to cast uint to int implicitly or require an explicit cast.

It's worth noting that this behavior (of a being less than b) follows the implicit conversion rules exactly: http://www.digitalmars.com/d/type.html [snipped non-applicable checks...] 5. Else the integer promotions are done on each operand, followed by: 1. If both are the same type, no more conversions are done. 2. If both are signed or both are unsigned, the smaller type is converted to the larger. 3. If the signed type is larger than the unsigned type, the unsigned type is converted to the signed type. 4. The signed type is converted to the unsigned type. So the int is implicitly converted to the uint, and (apparently) it simply compares 2**32-1 to 16. So I wouldn't call this a bug, just a potential oddity. Maybe it should detect and throw an overflow if a negative signed integer is converted to an unsigned type?

That would introduce a massive performance hit. The existing conversion from signed to unsigned occurs only at compile time. Or not: I'd consider this an edge case. It's
 probably considered bad practice to promiscuously mix signed and 
 unsigned types.

It's a hard one. It would be really painful if equality comparisons between signed & unsigned types was an error; it's almost always OK. Comparison of signed/unsigned variables with an unsigned/signed constant is always an error; it would be nice if the compiler detected it.
Jun 28 2006
parent reply Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> writes:
Don Clugston wrote:

 It's a hard one. It would be really painful if equality comparisons 
 between signed & unsigned types was an error; it's almost always OK.
 Comparison of signed/unsigned variables with an unsigned/signed constant 
 is always an error; it would be nice if the compiler detected it.

That was a comparison between an unsigned/signed CONSTANTS, that's the mess. They are foldable in that example.... --- Paolo
Jun 28 2006
parent SwiftCoder <SwiftCoder_member pathlink.com> writes:
It would seem that this is not unique to D, C++ also exhibits this behaviour
under GCC 4.0
I was just burn't by it in std::vector access.
Jun 29 2006