Consider this
int i = 2147483647;
var n = i + 3;
i = n;
Console.WriteLine(i); // prints -2147483646 (1)
Console.WriteLine(n); // prints -2147483646 (2)
Console.WriteLine(n.GetType()); // prints System.Int32 (3)
I am confused with following
(1) how could int hold the value -2147483646 ? (int range = -2,147,483,648 to 2,147,483,647)
(2) why does this print -2147483648 but not 2147483648 (compiler should
decide better type as int range
exceeds)
(3) if it is converted somewhere, why n.GetType() gives System.Int32
?
Edit1: Made the correction: Now you will get What I am Getting. (sorry for that)
var n = i + 1; to
var n = i + 3;
Edit2: One more thing, if it as overflow, why is an exception not raised ?
Addition: as the overflow occurs, is it not right to set the type for
var n
in statement var n = i + 3; to another type accordingly ?
you are welcome to suggest a better title, as this is not making sense to.... me at least
Thanks
Update: Poster fixed his question.
1) This is output is expected because you added 3 to int.MaxValue causing an overflow. In .NET by default this is a legal operation in unchecked code giving a wrap-around to negative values, but if you add a checked block around the code it will throw an OverflowException instead.
2) The type of a variable declared with var is determined at compile time not runtime. It's a rule that adding two Int32s gives an Int32, not a UInt32, an Int64 or something else. So even though at runtime you can see that the result is too big for an Int32, it still has to return an Int32.
3) It's not converted to another type.
1) -2147483646 is bigger than -2,147,483,648
2) 2147483648 is out of range
3) int is an alias for Int32
1)
First of all, the value in the variable is not -2147483646, it's -2147483648. Run your test again and check the result.
There is no reason that an int could not hold the value -2147483646. It's within the range -2147483648..2147483647.
2)
The compiler chooses the data type of the variable to be the type of the result of the expression. The expression returns an int value, and even if the compiler would choose a larger data type for the variable, the expression still returns an int and you get the same value as result.
It's the operation in the expression that overflows, it's not when the result is assigned to the variable that it overflows.
3)
It's not converted anywhere.
This is an overflow, your number wrapped around and went negative
This isn't the compiler's job, as a loop at runtime can cause the same thing
int is an alias or System.Int32 they are equivalent in .Net.
This is because of the bit representation
you use Int32 but the same goes for char (8 bits)
the first bit holds the sign, then the following bits hold the number
so with 7 bits you can represent 128 numbers 0111 1111
when you try the 129th, 1000 0001, the sign bits get set so the computer thinks its -1 instead
Arithmic operations in .NET don't change the actual type.
You start off with an (32bit) integer and the +3 isn't going to change that.
That's also why you get an unexpected round number when you do this:
int a = 2147483647;
double b = a / 4;
or
int a = 2147483647;
var b = a / 4;
for that matter.
EDIT:
There is no exception because .NET overflows the number.
The overflow exception will only occur at assignment operations or as Mark explains when you set the conditions to generate the exception.
If you want an exception to be thrown, write
abc = checked(i+3)
instead. That will check for overflows.
Also, in c#, the default setting is to not throw exceptions on overflows. But you can switch that option somewhere on your project's properties.
You could make this easier on us all by using hex notation.
Not everyone knows that the eighth Mersenne prime is 0x7FFFFFFF
Just sayin'
Related
I'm a little confused about the behavior of the ulong type. I understand it to be a 64-bit integer used for positive numbers (max value 18,446,744,073,709,551,615). I just started using it because I need really big positive numbers, but there's some strange behavior I don't understand around potential negative numbers.
ulong big = 1000000; //Perfectly legal
ulong negative = -1; //"Constant value "-1" cannot be converted to a ulong" Makes sense
//Attempt to sneak around the -1 as a constant compile-time error
int negativeInt = -1;
ulong negativeUlongFail = negativeInt; //"Cannot implicitly convert 'int' to 'ulong'.
//An explicit conversion exists (are you missing a cast?)"
//So I add casting
ulong negativeUlong = (ulong)negativeInt; //But this yields 18446744073709551615
//Try and get a negative number indirectly
ulong number = 0;
number = number - 10; //now number is 18446744073709551615 but no underflow error
What's going on? How come some underflow errors are caught and others aren't and don't even throw exceptions? Can I detect these?
I focused on getting negative numbers by underflowing, but I've seen similar things when getting numbers bigger than the max value and overflowing.
I'm not sure if they're technically Errors or Exceptions, so please forgive me if I used incorrect terminology
if you want the underflow (or overflow) exception to be thrown, try this instead :-
checked
{
ulong number = 0;
number = number - 10;
}
by default the exception gets swallowed (it runs in unchecked).
see official documentation
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/checked
I have this expression
long balance = (long)answer.Find(DppGlobals.TAG_TI_BALANCE).Get_QWORD();
which raises exception that there was overflow. The value on the right hand side is of unsigned type and has value: 18446744073708240732.
How to avoid this exception, use unchecked?
PS equivalent C++ implementation returned balance = -1310984, I need same value here too.
Why is there such an exception?
By using unchecked indeed now also on C# side I get -1310984. Can someone advice, am I losing data somehow?
Using uncheck will also crop your value, but you will not have an exception,you will get wrong number.I think you will do something changing your code, not mask your exception.
long balance = (unchecked))(long)answer.Find(DppGlobals.TAG_TI_BALANCE).Get_QWORD());
Why is there such an exception?
It is a very good that you get an error.Because if you are working in the sphere where everything is numbers, you can have a mistake and don't understand this.So the RunTime will show you your eror
Given the comments, it sounds like you do just need to use unchecked arithmetic - but you should be concerned about the use of ulong in your API. If your aim is to just propagate 8 bytes of data, and interpret it as either an unsigned integer or a signed integer depending on context, then you're fine - so long as nothing performs arithmetic on it in the "wrong" form.
It's important to understand why this happens though, and it's much easier to explain that with small numbers. I'll use byte and sbyte as an example. The range of byte is 0 to 255 inclusive. The range of sbyte is -128 to 127 inclusive. Both can store 256 different values. No problem.
Now, using unchecked conversions, it's fine to say:
byte a = GetByteFromSomewhere();
sbyte b = (sbyte) a;
StoreSByteSomewhere(b);
...
sbyte b = GetSByteFromStorage();
byte a = (byte) b;
If that's all you're doing, that's fine. A byte value of -1 will become an sbyte value of 255 and vice versa - basically it's just interpreting the same bit pattern in different ways.
But if you're performing other operations on the value when it's being handled as the "wrong" type, then you could get unexpected answers. For example, consider "dividing by 3". If you divide -9 by 3, you get -3, right? Whereas if you have:
byte original = -9;
sbyte converted = (sbyte) original;
sbyte divided = converted / 3;
byte result = (byte) divided;
... then you end up with a result of -2 instead of -3, due to the way the arithmetic worked.
Now that's all for unchecked conversions. When you have a checked conversion, it doesn't just interpret the bits - it treats the value as a number instead. So for example:
// In a checked context...
byte a = 128;
sbyte b = (sbyte) a; // Bang! Exception
That's because 128 (as a number) is outside the range of sbyte. This is what's happening in your case - the number 18446744073708240732 is outside the range of long, so you're getting an exception. The checked conversion is treating it as a number which can be range-checked, rather than an unchecked conversion just reinterpreting the bits as a long (which leads to the negative number you want).
Consider the following code. First 5 iterations goes well and then it goes on in an infinite loop spamming 0's.
for (int i = 2; i < 100000; i*=i)
{
Console.WriteLine(i);
}
I'm suspecting its because the Integer overflows, and guessing that the default returned value when it overflows is 0.
How would you write this code, so that it does what it is intended to do?
Edit: This is kinda a theoretical example, and i'm just playing around to learn. Is there no way to do this, without changing datatype or checking for 0? Would be nice if it actually threw an exception.
I believe you're right that it overflows.
Rather than just telling you to use unchecked to force it to throw an exception, here is an explanation of WHY you get zeros after the 5th result
Here is the series:
2
4
16
256
65536
4294967296 <- this won't fit into an int
in your code, 2,147,483,648 + 1 becomes -2,147,483,648
the 6th value overflows and becomes 0 (4,294,967,296 ends up going from 0 to 2,147,483,648 to -2,147,483,648 and keeps adding back up towards 0 again)
and your for modifier clause will then run 0*=0 indefinitely
EDIT: as per your update, use the checked keyword to get an exception on the overflow
checked {
for (int i = 2; i < 100000; i*=i)
{
Console.WriteLine(i);
}
}
otherwise use a long or ulong instead of int as both of those can safely store the result of 100000*100000
Use checked if you want an overflow exception:
checked
{
for (int i = 2; i < 100000; i*=i)
{
Console.WriteLine(i);
}
}
The checked keyword is used to explicitly enable overflow checking for
integral-type arithmetic operations and conversions.
By default, an expression that contains only constant values causes a
compiler error if the expression produces a value that is outside the
range of the destination type. If the expression contains one or more
non-constant values, the compiler does not detect the overflow.
I am doing: -
Decimal production = 0;
Decimal expense = 5000;
Decimal.ToUInt64(production - expense);
But it throws exception with the following error message.
"Value was either too large or too small for a UInt64"
Can someone give me a workaround for this.
Thanks!
Edit
In any case, I want the result as a positive number.
Problem: -5000m is a negative number, which is outside the range of UInt64 (an unsigned type).
Solution: use Int64 instead of UInt64 if you want to cope with negative numbers.
Note that you can just cast instead of calling Decimal.To...:
long x = (long) (production - expense);
Alternative: validate that the number is non-negative before trying to convert it, and deal with it however you deem appropriate.
Very dodgy alternative: if you really just want the absolute value (which seems unlikely) you could use Math.Abs:
UInt64 alwaysNonNegative = Decimal.ToUInt64(Math.Abs(production - expense));
0 - 5000 will return -5000. And you are trying to convert to an unsigned int which can not take negative values.
Try changing it to signed int
Decimal.ToInt64(production - expense);
UInt can not store negative numbers. The result of your calculation is negative. That's why the error comes. Check the sign before using ToUInt64 and correct it via *-1 or use a signed Int64.
use
var result = Decimal.ToInt64(production - expense);
This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
byte + byte = int… why?
I have a method like this:
void Method(short parameter)
{
short localVariable = 0;
var result = localVariable - parameter;
}
Why is the result an Int32 instead of an Int16?
It's not just subtraction, there simply exisits no short (or byte/sbyte) arithmetic.
short a = 2, b = 3;
short c = a + b;
Will give the error that it cannot convert int (a+b) to short (c).
One more reason to almost never use short.
Additional: in any calculation, short and sbyte will always be 'widened' to int, ushort and byte to uint. This behavior goes back to K&R C (and probaly is even older than that).
The (old) reason for this was, afaik, efficiency and overflow problems when dealing with char. That last reason doesn't hold so strong for C# anymore, where a char is 16 bits and not implicitly convertable to int. But it is very fortunate that C# numerical expressions remain compatible with C and C++ to a very high degree.
All operations with integral numbers smaller than Int32 are widened to 32 bits before calculation by default. The reason why the result is Int32 is simply to leave it as it is after calculation. If you check the MSIL arithmetic opcodes, the only integral numeric type they operate with are Int32 and Int64. It's "by design".
If you desire the result back in Int16 format, it is irrelevant if you perform the cast in code, or the compiler (hypotetically) emits the conversion "under the hood".
Also, the example above can easily be solved with the cast
short a = 2, b = 3;
short c = (short) (a + b);
The two numbers would expand to 32 bits, get subtracted, then truncated back to 16 bits, which is how MS intended it to be.
The advantage of using short (or byte) is primarily storage in cases where you have massive amounts of data (graphical data, streaming, etc.)
P.S. Oh, and the article is "a" for words whose pronunciation starts with a consonant, and "an" for words whose pronunciated form starts with a vowel. A number, AN int. ;)
The other answers given within this thread, as well as the discussions given here are instructive:
(1) Why is a cast required for byte subtraction in C#?
(2) byte + byte = int… why?
(3) Why is a cast required for byte subtraction in C#?
But just to throw another wrinkle into it, it can depend on which operators you use. The increment (++) and decrement (--) operators as well as the addition assignment (+=) and subtraction assignment (-=) operators are overloaded for a variety of numeric types, and they perform the extra step of converting the result back to the operand's type when returning the result.
For example, using short:
short s = 0;
s++; // <-- Ok
s += 1; // <-- Ok
s = s + 1; // <-- Compile time error!
s = s + s; // <-- Compile time error!
Using byte:
byte b = 0;
b++; // <-- Ok
b += 1; // <-- Ok
b = b + 1; // <-- Compile time error!
b = b + b; // <-- Compile time error!
If they didn't do it this way, calls using the increment operator (++) would be impossible and calls to the addition assignment operator would be awkward at best, e.g.:
short s
s += (short)1;
Anyway, just another aspect to this whole discussion...
I think its done automatically done to avoid overflow,
lets say you do something like this.
Short result = Short.MaxValue + Short.MaxValue;
The result clearly wouldn't fit in a short.
one thing i don't understand is then why not do it for int32 too which would automatically convert to long???
The effect you are seeing...
short - short = int
...is discussed extensively in this Stack Overflow question: byte + byte = int… why?
There is a lot of good information and some interesting discussions as to why it is that way.
Here is a highly-voted answer:
I believe it's basically for the sake
of performance. (In terms of "why it
happens at all" it's because there
aren't any operators defined by C# for
arithmetic with byte, sbyte, short or
ushort, just as others have said. This
answer is about why those operators
aren't defined.)
Processors have native operations to
do arithmetic with 32 bits very
quickly. Doing the conversion back
from the result to a byte
automatically could be done, but would
result in performance penalties in the
case where you don't actually want
that behaviour.
-- Jon Skeet