4
public void Foo(double d){
    // when called below, d == 2^32-1 
    ...
}
public void Bar(){
    uint ui = 1;
    Foo( 0 - ui );
}

I would expect both 0 and ui to be promoted to signed longs here.

True, with the 0 literal it is knowable at compile time that a cast to uint is safe,

but I suppose this all just seems wrong. At least a warning should be issued.

Thanks!

Does the language spec cover a semi-ambiguous case like this?

some bits flipped
  • 2,592
  • 4
  • 27
  • 42

3 Answers3

7

Why would anything be promoted to long? The spec (section 7.8.5) lists four operators for integer subtraction:

  • int operator-(int x, int y);
  • uint operator-(uint x, uint y);
  • long operator-(long x, long y);
  • ulong operator-(ulong x, ulong y);

Given that the constant value 0 is implicitly convertible to uint, but the uint value ui is not implicitly convertible to int, the second operator is chosen according to the binary operator overload resolution steps described in section 7.3.4.

(Is it possible that you were unaware of the implicit constant expression conversion from 0 to uint and that that was the confusing part? See section 6.1.9 of the C# 4 spec for details.)

Following section 7.3.4 (which then refers to 7.3.5, and 7.5.3) is slightly tortuous, but I believe it's well-defined, and not at all ambiguous.

If it's the overflow that bother you, would expect this to fail as well?

int x = 10;
int y = int.MaxValue - 5;
int z = x + y;

If not, what's really the difference here?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think his issue is more with the (potentially) ambiguous resultant value. i.e. If I subtract a positive number from 0 I should not get a positive result. Similarly with your int example, adding 10 to a positive number should not result in a negative number. A question I would have, if you are dealing with these types of bounds on your numbers wouldn't it be best to use `checked` to assure that your result isn't ambiguous (at least ambiguous to you) – NominSim Mar 01 '12 at 23:29
  • @NominSim: There's no ambiguity here. It's behaving *exactly* as the specification dictates it should. If the OP wants to use a checked context then they absolutely can, but there's no need for a warning here, and it's all behaving correctly. I believe the OP's *actual* issue is that it's being performed with `uint` arithmetic whereas he expected it to use `long` arithmetic: "I would expect both 0 and ui to be promoted to signed longs here." – Jon Skeet Mar 02 '12 at 00:06
  • I get that it is behaving exactly as the specification dictates, the ambiguity isn't in the spec but rather in the action a la if I subtract a guaranteed positive number from a negative number I should get a negative result. That's why I think the OP should use the checked context if they are dealing with numbers that may lead out of the bounds of the particular data structure. – NominSim Mar 02 '12 at 13:41
  • @NominSim: *You* understand that it's as per the spec, but I don't believe the OP did. I don't think the word "ambiguity" is helpful here - there simply isn't any. There may be unexpected or undesirable behaviour, but that's not the same as ambiguity. – Jon Skeet Mar 02 '12 at 13:44
  • That's true, I am using the word since the OP used it. Now that I think more about it too, his statement that "At least a warning should be issued" at first seemed reasonable to me, but the more I think about it the more I agree with you that it is fine as is, hand-holding can only get you so far... – NominSim Mar 02 '12 at 13:51
  • Thanks for the detailed response @JonSkeet. I accepted the answer that most concisely summarized the problem, which was that 0 is being interpreted by the compiler as unsigned. My comment about a warning still stands - the compiler is doing an implicit conversion in such a way that almost guarantees underflow, and in c# it seems this should be a warning... (as opposed to c/c++, where I would NOT expect one. I'm still early on the learning curve for C#, though.) – some bits flipped Mar 06 '12 at 22:22
  • @mike: I suggest you consider *exactly* what change you'd expect to see in the language specification, without making it significantly more complex. – Jon Skeet Mar 06 '12 at 22:26
0

It's the int that is being cast to uint to perform substraction from 0 (which is implicitly interpreted by the compiler as uint). Note that int to uint is an implicit conversion hence no warning. There is nothing wrong with your code... except that uint is not CLS-compilant. You can read why here. More info on CLS-compilant code on MSDN

Community
  • 1
  • 1
SiliconMind
  • 2,185
  • 4
  • 25
  • 49
  • 1
    `int` to `uint` isn't generally an implicit conversion - this is an *implicit constant expression conversion*. See section 6.1.9 of the spec. If we'd started off with an `int` *variable*, then there'd have been promotion to `long`. (Both `int` and `uint` can be implicitly converted to `long`.) – Jon Skeet Mar 01 '12 at 23:20
  • Accepting this answer because the root cause of my confusion is simply that zero is implicitly convertible to uint. I expected promotion to long based on the assumption that 0 was int... – some bits flipped Mar 06 '12 at 22:18
0

In a checked context, if the difference is outside the range of the result type, a System.OverflowException is thrown. In an unchecked context, overflows are not reported and any significant high-order bits outside the range of the result type are discarded.

http://msdn.microsoft.com/en-us/library/aa691376(v=vs.71).aspx

Technically, doing the following:

double d = checked(0-ui);

Will result in a throw of System.OverflowException which is perhaps what you are expecting, but according to the spec since this is not checked the overflow is not reported.

NominSim
  • 8,447
  • 3
  • 28
  • 38