Unchecked block not working with BigInteger?

I just noticed that the unchecked context does not work when working with BigInteger, for example:

unchecked { // no exception, long1 assigned to -1 as expected var long1 = (long)ulong.Parse(ulong.MaxValue.ToString()); } unchecked { var bigInt = BigInteger.Parse(ulong.MaxValue.ToString()); // throws overflow exception var long2 = (long)bigInt; } 

Any idea why this is the case? Is there anything special about how large integers are converted to other primitive integer types?

Thanks,

+4
source share
3 answers

The C # compiler does not know that BigInteger is logically an "integral type". It just sees a user type with explicit user-defined conversion to long. From the point of view of the compiler

 long long2 = (long)bigInt; 

exactly the same as:

 long long2 = someObject.SomeMethodWithAFunnyNameThatReturnsALong(); 

He has no way to get inside this method and tell him to stop throwing exceptions.

But when the compiler sees

 int x = (int) someLong; 

the compiler generates the code that performs the conversion, so it can choose to create verified or unverified code as it sees fit.

Remember that "checked" and "unchecked" do not affect runtime; it is not like the CLR goes into "unchecked mode" when control goes into an uncontrolled context. "checked" and "unchecked" are instructions for the compiler about what code is generated inside the block. They only affect compilation time, and the compilation of the conversion of BigInt to long has already occurred. His behavior is fixed.

+18
source

OverflowException is actually thrown by an explicit casting operator defined on BigInteger . It looks like this:

 int num = BigInteger.Length(value._bits); if (num > 2) { throw new OverflowException(SR.GetString("Overflow_Int64")); } 

In other words, it handles overflows this way, regardless of the context checked or unchecked . Documents really say that.

Update . Of course, Eric is the last word. Please read his post :)

+4
source

The documentation explicitly states that in this situation, it will throw an OverflowException . The checked context only affects the "native" arithmetic operations that the C # compiler emits, which does not include calls to explicit conversion operators.

To perform the conversion "safely", you must first compare it with long.MaxValue and long.MinValue to check if it is in a range. To get the overflow effect to negative, I suspect you will have to use bitwise operators within BigInteger . For instance:

 using System; using System.Numerics; class Program { static void Main(string[] args) { BigInteger bigValue = new BigInteger(ulong.MaxValue); long x = ConvertToInt64Unchecked(bigValue); Console.WriteLine(x); } private static readonly BigInteger MaxUInt64AsBigInteger = ulong.MaxValue; private static long ConvertToInt64Unchecked(BigInteger input) { unchecked { return (long) (ulong) (input & MaxUInt64AsBigInteger); } } } 
+2
source

Source: https://habr.com/ru/post/1369286/


All Articles