www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - (int << ulong) == int ?

reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
Just lost the best part of an hour figuring the cause of this small 
problem, consider:

void main()
{
         uint j = 42;
         ulong k = 1<<cast(ulong)j;
         ulong m = 1UL<<j;
         assert(k == 1024);//both asserts do pass
         assert(m == (1UL<<42));
}

I though left operand should be promoted to the largest integer in shift 
expression, isn't it?

-- 
Dmitry Olshansky
Aug 07 2011
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 08 August 2011 00:33:31 Dmitry Olshansky wrote:
 Just lost the best part of an hour figuring the cause of this small
 problem, consider:
 
 void main()
 {
          uint j = 42;
          ulong k = 1<<cast(ulong)j;
          ulong m = 1UL<<j;
          assert(k == 1024);//both asserts do pass
          assert(m == (1UL<<42));
 }
 
 I though left operand should be promoted to the largest integer in shift
 expression, isn't it?

I would not expect that type of integer being used to give the number of bits to shift to affect thet type of integer being shifted. It doesn't generally make much sense to shift more than the size of the integer type being shifted, and that can always fit in a byte. So, why would the type of the integer being used to give the number of bits to shift matter? It's like an index. The index doesn't affect the type of what's being indexed. It just gives you an index. You have to deal with integer promotions and all that when doing arithmetic, because arithmetic needs to be done with a like number of bits on both sides of the operation. But with shifting, all your doing is asking it to shift some number of bits. The type which holds the number of bits shouldn't really matter. I wouldn't expect _any_ integer promotions to occur in a shift expression. If you want to affect what's being shifted, then cast what's being shifted. - Jonathan M Davis
Aug 07 2011
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
 You have to deal with integer promotions and all that when doing arithmetic,
 because arithmetic needs to be done with a like number of bits on both sides
 of the operation. But with shifting, all your doing is asking it to shift some
 number of bits. The type which holds the number of bits shouldn't really
 matter. I wouldn't expect _any_ integer promotions to occur in a shift
 expression. If you want to affect what's being shifted, then cast what's being
 shifted.

That's right... Somehow it didn't occur to me till the last moment that bit shift is asymmetric in nature so it really shouldn't do any promotions. Sorry for the noise. -- Dmitry Olshansky
Aug 07 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Dmitry Olshansky:

 Sorry for the noise.

It's not noise, and you don't need to be sorry, in my opinion it's a D/DMD design fault. Clang gives an error on code like that: http://blog.llvm.org/2011/05/c-at-google-here-be-dragons.html See the error: example2.cc:12:25: error: shift result (10737418240) requires 35 bits to represent, but 'int' only has 32 bits [-Werror,-Wshift-overflow] long kMaxDiskSpace = 10 << 30; In my opinion D has to give a similar error. Bye, bearophile
Aug 07 2011
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 08.08.2011 2:07, bearophile wrote:
 Dmitry Olshansky:

 Sorry for the noise.

http://blog.llvm.org/2011/05/c-at-google-here-be-dragons.html See the error: example2.cc:12:25: error: shift result (10737418240) requires 35 bits to represent, but 'int' only has 32 bits [-Werror,-Wshift-overflow] long kMaxDiskSpace = 10<< 30; In my opinion D has to give a similar error.

It's useful but not a panacea. I presented a simple trimmed down example with consts, I don't think compiler can reliably identify the problem in my actual code. -- Dmitry Olshansky
Aug 07 2011
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Brad Roberts:

 Which has nothing to do with the question about integral promotion.

The original code was similar to this C code: int main() { unsigned int j = 42; unsigned long long k = 1 << (unsigned long long)j; return k; } If you process it with a simple C lint you get a warning 647: 1 int main() { 2 unsigned int j = 42; 3 unsigned long long k = 1 << (unsigned long long)j; 3 Info 701: Shift left of signed quantity (int) 3 Warning 647: Suspicious truncation 4 return k; 4 Info 712: Loss of precision (return) (unsigned long long to int) 5 } I think a compiler able to detect similar cases helps the OP avoid asking the question "I though left operand should be promoted to the largest integer in shift expression, isn't it?". -------------- Dmitry Olshansky:
 It's useful but not a panacea.

I agree, you need something more/better to improve the situation a lot. Bye, bearophile
Aug 07 2011
prev sibling parent reply Don <nospam nospam.com> writes:
Jonathan M Davis wrote:
 On Monday 08 August 2011 00:33:31 Dmitry Olshansky wrote:
 Just lost the best part of an hour figuring the cause of this small
 problem, consider:

 void main()
 {
          uint j = 42;
          ulong k = 1<<cast(ulong)j;
          ulong m = 1UL<<j;
          assert(k == 1024);//both asserts do pass
          assert(m == (1UL<<42));
 }

 I though left operand should be promoted to the largest integer in shift
 expression, isn't it?

I would not expect that type of integer being used to give the number of bits to shift to affect thet type of integer being shifted. It doesn't generally make much sense to shift more than the size of the integer type being shifted, and that can always fit in a byte. So, why would the type of the integer being used to give the number of bits to shift matter? It's like an index. The index doesn't affect the type of what's being indexed. It just gives you an index. You have to deal with integer promotions and all that when doing arithmetic, because arithmetic needs to be done with a like number of bits on both sides of the operation. But with shifting, all your doing is asking it to shift some number of bits. The type which holds the number of bits shouldn't really matter. I wouldn't expect _any_ integer promotions to occur in a shift expression. If you want to affect what's being shifted, then cast what's being shifted.

Your intuition is wrong! expression.html explicitly states the operands to shifts undergo integral promotions. But they don't get arithmetic conversions. I think this is terrible. short x = -1; x >>>= 1; Guess what x is...
Aug 09 2011
parent Don <nospam nospam.com> writes:
Jonathan M Davis wrote:
 On Tuesday 09 August 2011 09:32:41 Don wrote:
 Jonathan M Davis wrote:
 On Monday 08 August 2011 00:33:31 Dmitry Olshansky wrote:
 Just lost the best part of an hour figuring the cause of this small
 problem, consider:

 void main()
 {

          uint j = 42;
          ulong k = 1<<cast(ulong)j;
          ulong m = 1UL<<j;
          assert(k == 1024);//both asserts do pass
          assert(m == (1UL<<42));

 }

 I though left operand should be promoted to the largest integer in
 shift
 expression, isn't it?

bits to shift to affect thet type of integer being shifted. It doesn't generally make much sense to shift more than the size of the integer type being shifted, and that can always fit in a byte. So, why would the type of the integer being used to give the number of bits to shift matter? It's like an index. The index doesn't affect the type of what's being indexed. It just gives you an index. You have to deal with integer promotions and all that when doing arithmetic, because arithmetic needs to be done with a like number of bits on both sides of the operation. But with shifting, all your doing is asking it to shift some number of bits. The type which holds the number of bits shouldn't really matter. I wouldn't expect _any_ integer promotions to occur in a shift expression. If you want to affect what's being shifted, then cast what's being shifted.

expression.html explicitly states the operands to shifts undergo integral promotions. But they don't get arithmetic conversions. I think this is terrible. short x = -1; x >>>= 1; Guess what x is...

That's just downright weird. Why would any integral promotions occur with a shift? And given your example, the current behavior seems like a bad design. Is this some weird hold-over from C/C++?

I think it must be, some silly idea of 'simplifying' things by applying the same promotion rules to all binary operators. Given that 256-bit integers still don't seem to be coming any time soon, even shifts by a signed byte shouldn't require promotion. Incidentally, the x86 instruction set doesn't use integers, you can only shift a 64-bit number by an 8-bit value. So x << y always compiles to x << cast(ubyte)y. Shift should not need a common type. This is one of those things that can cause problems, but never helps.
Aug 09 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 08 August 2011 01:42:37 Dmitry Olshansky wrote:
 You have to deal with integer promotions and all that when doing
 arithmetic, because arithmetic needs to be done with a like number of
 bits on both sides of the operation. But with shifting, all your doing
 is asking it to shift some number of bits. The type which holds the
 number of bits shouldn't really matter. I wouldn't expect _any_ integer
 promotions to occur in a shift expression. If you want to affect what's
 being shifted, then cast what's being shifted.

That's right... Somehow it didn't occur to me till the last moment that bit shift is asymmetric in nature so it really shouldn't do any promotions. Sorry for the noise.

Yeah, well. Similar stuff happens to us all upon occasion. The real problem is when it happens frequently - Jonathan M Davis
Aug 07 2011
prev sibling next sibling parent Brad Roberts <braddr puremagic.com> writes:
Which has nothing to do with the question about integral promotion. =20

On Aug 7, 2011, at 3:07 PM, bearophile <bearophileHUGS lycos.com> wrote:

 Dmitry Olshansky:
=20
 Sorry for the noise.

It's not noise, and you don't need to be sorry, in my opinion it's a D/DMD=

 http://blog.llvm.org/2011/05/c-at-google-here-be-dragons.html
=20
 See the error:
 example2.cc:12:25: error: shift result (10737418240) requires 35 bits to r=

 long kMaxDiskSpace =3D 10 << 30;
=20
 In my opinion D has to give a similar error.
=20
 Bye,
 bearophile

Aug 07 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday 09 August 2011 09:32:41 Don wrote:
 Jonathan M Davis wrote:
 On Monday 08 August 2011 00:33:31 Dmitry Olshansky wrote:
 Just lost the best part of an hour figuring the cause of this small
 problem, consider:
 
 void main()
 {
 
          uint j = 42;
          ulong k = 1<<cast(ulong)j;
          ulong m = 1UL<<j;
          assert(k == 1024);//both asserts do pass
          assert(m == (1UL<<42));
 
 }
 
 I though left operand should be promoted to the largest integer in
 shift
 expression, isn't it?

I would not expect that type of integer being used to give the number of bits to shift to affect thet type of integer being shifted. It doesn't generally make much sense to shift more than the size of the integer type being shifted, and that can always fit in a byte. So, why would the type of the integer being used to give the number of bits to shift matter? It's like an index. The index doesn't affect the type of what's being indexed. It just gives you an index. You have to deal with integer promotions and all that when doing arithmetic, because arithmetic needs to be done with a like number of bits on both sides of the operation. But with shifting, all your doing is asking it to shift some number of bits. The type which holds the number of bits shouldn't really matter. I wouldn't expect _any_ integer promotions to occur in a shift expression. If you want to affect what's being shifted, then cast what's being shifted.

Your intuition is wrong! expression.html explicitly states the operands to shifts undergo integral promotions. But they don't get arithmetic conversions. I think this is terrible. short x = -1; x >>>= 1; Guess what x is...

That's just downright weird. Why would any integral promotions occur with a shift? And given your example, the current behavior seems like a bad design. Is this some weird hold-over from C/C++? - Jonathan M Davis
Aug 09 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday 09 August 2011 11:03:11 Don wrote:
 Jonathan M Davis wrote:
 On Tuesday 09 August 2011 09:32:41 Don wrote:
 Jonathan M Davis wrote:
 On Monday 08 August 2011 00:33:31 Dmitry Olshansky wrote:
 Just lost the best part of an hour figuring the cause of this
 small
 problem, consider:
 
 void main()
 {
 
          uint j = 42;
          ulong k = 1<<cast(ulong)j;
          ulong m = 1UL<<j;
          assert(k == 1024);//both asserts do pass
          assert(m == (1UL<<42));
 
 }
 
 I though left operand should be promoted to the largest integer in
 shift
 expression, isn't it?

I would not expect that type of integer being used to give the number of bits to shift to affect thet type of integer being shifted. It doesn't generally make much sense to shift more than the size of the integer type being shifted, and that can always fit in a byte. So, why would the type of the integer being used to give the number of bits to shift matter? It's like an index. The index doesn't affect the type of what's being indexed. It just gives you an index. You have to deal with integer promotions and all that when doing arithmetic, because arithmetic needs to be done with a like number of bits on both sides of the operation. But with shifting, all your doing is asking it to shift some number of bits. The type which holds the number of bits shouldn't really matter. I wouldn't expect _any_ integer promotions to occur in a shift expression. If you want to affect what's being shifted, then cast what's being shifted.

Your intuition is wrong! expression.html explicitly states the operands to shifts undergo integral promotions. But they don't get arithmetic conversions. I think this is terrible. short x = -1; x >>>= 1; Guess what x is...

That's just downright weird. Why would any integral promotions occur with a shift? And given your example, the current behavior seems like a bad design. Is this some weird hold-over from C/C++?

I think it must be, some silly idea of 'simplifying' things by applying the same promotion rules to all binary operators. Given that 256-bit integers still don't seem to be coming any time soon, even shifts by a signed byte shouldn't require promotion. Incidentally, the x86 instruction set doesn't use integers, you can only shift a 64-bit number by an 8-bit value. So x << y always compiles to x << cast(ubyte)y. Shift should not need a common type. This is one of those things that can cause problems, but never helps.

Given that fact, wouldn't it be safe to change the behavior so that it _doesn't_ do promotions? It sounds like the only case where it would change behavior (either for code ported from C/C++ or for existing D code) is if it's doing something wrong. - Jonathan M Davis
Aug 09 2011