Reputation: 65
At least on the surface, this is not a question about overflowing int nor uint. I understand the values overlap by only ~half, and the ranges are equal. In fact I'm counting on that.
Background: Have a DotNet CRC hash algorithm that returns an uint, and I need to convert that for storage in sql (which lacks an uint). This conversion should always be possible as the total range of both are equal, even though the start and end points are different. There is no concern about reversing the conversion. I Debuggered this as DotNet 4.0 and 4.6.1 with same results.
In the code examples below:
intForSqlStorageOK is successful.
But intForSqlStorageFail1 throws a runtime exception.
What's different about them?
{
uint uIntToBeStoredInSql = 591071; //CRC32 hash result
int intMaxValue = int.MaxValue;
int intForSqlStorageOK = Convert.ToInt32(uIntToBeStoredInSql - intMaxValue - 1);
//System.OverflowException: 'Value was either too large or too small for an Int32.'
int intForSqlStorageFail1 = Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1);
int intForSqlStorageFail2 = Convert.ToInt32(uIntToBeStoredInSql - Convert.ToUInt32(int.MaxValue) - 1);
int intForSqlStorageFail3 = checked(Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1));
int intForSqlStorageFail4 = unchecked(Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1));
int intForSqlStorageFail5; //??? probably many others ???
///this SO led to this workaround, which is just clobbering the problem by adding memory. doesn't explain the difference above.
///https://stackoverflow.com/questions/26386201/subtracting-uint-and-int-and-constant-folding
int intForSqlStorageOK2 = Convert.ToInt32(Convert.ToInt64(uIntToBeStoredInSql) - int.MaxValue - 1);
///Thanks ckuri in comments - this is way more terse and somehow still arrives at same result
int intForSqlStorageOK3 = (int)uIntToBeStoredInSql - intMaxValue - 1;
int intForSqlStorageOK4 = (int)uIntToBeStoredInSql - int.MaxValue - 1;
}
Thanks everyone for commenting! learned alot
Upvotes: 1
Views: 377
Reputation: 22132
According to specification, int.MaxValue
can be implicitly converted to uint
type, while non-constant int
expression can not, because at compile time compiler does not know if value of int
expression within range of uint
type or not.
If you apply rules for operator resolution, then you will see, that uIntToBeStoredInSql - intMaxValue - 1
is interpreted as (long)uIntToBeStoredInSql - (long)intMaxValue - 1L
with overall value -2146892577L
(which is in range of int
type).
While uIntToBeStoredInSql - int.MaxValue - 1
is interpreted as uIntToBeStoredInSql - (uint)int.MaxValue - 1u
with overall value 2148074719u
(which is not in range of int
type) in unchecked
context and OverflowException
exception in checked
context.
Upvotes: 2