Why does C # allow me to overflow without any errors or warnings when sending Int64 to Int32 and how to do it?

This question is related to an error in which I repeated the Int64 set and accidentally made foreach (int i in myCollection) . I tried to debug an incomprehensible problem about how, when executing the query, linq i not part of myCollection .

Here is the code that surprised me:

 Int64 a = 12345678912345; Console.Write((int)a); 

I was expecting the compiler to give me an error. It is common that an implicit throw does not exist. But no, this is not at all against. Even a warning!

The output value (int)a way is 1942903641 .

I am curious to know why the actors are allowed without any warnings, as well as how this happens with this value. Any ideas?

+6
source share
6 answers

By default, C # does not check overflow when processing numbers. This includes things like wrapping from int.MaxValue to int.MinValue in addition and multiplying, as well as when long pressed on int s. To control this, use the checked and unchecked keywords or the /checked option for the compiler.

The value 1942903641 is the result when your long truncated to int . It comes from the 32 least significant bits of the long value, taken as two additions , indicated by an integer.

When using foreach it is important to know that if you declare a type that does not match an enumerated type, it will process it as if you were using that type. foreach (int i in myCollection) compiles into something like int i = (int)myEnumerator.Current; , not int i = myEnumerator.Current; . You can use foreach (var i in myCollection) to avoid such errors in the future. var recommended for use with loop variables in for and foreach expressions.

You can see the results of various things in the following example (hexadecimal output is used to display truncation more clearly: they have the same final digits, int just lacks some more significant digits):

 checked { Int64 a = 12345678912345; Console.WriteLine(a.ToString("X")); Console.WriteLine((a % ((long)uint.MaxValue + 1L)).ToString("X")); try { Console.WriteLine(((int)a).ToString("X")); // throws exception } catch (Exception e) { Console.WriteLine("It threw! " + e.Message); } } unchecked { Int64 a = 12345678912345; Console.WriteLine(a.ToString("X")); Console.WriteLine((a % (long)Math.Pow(2, 32)).ToString("X")); Console.WriteLine(((int)a).ToString("X")); } 

It is output:

 B3A73CE5B59 73CE5B59 It threw! Arithmetic operation resulted in an overflow. B3A73CE5B59 73CE5B59 73CE5B59 
+6
source

By default, such conversions are not checked. You should ask for this explicitly:

  Console.Write(checked((int)a)); // Kaboom! 

Verified conversions can be enabled globally using the C # compiler / checked option . To prevent project templates from including this option, since the Debug assembly is an oversight in my book, overflowing can be very difficult to diagnose.

However, you cannot fix anything, just use "Project"> "Properties"> "Build" tab> "Advanced"> check the option "Check arithmetic overflow / underflow". And notice how your foreach loop is now bombing with an OverflowException. Keep in mind that you will look at it for a couple of minutes when this happens :) This is not a very cheap check, so you will want to leave it to build Release.

+13
source

By default, overflow checking is disabled for arithmetic operations of integral type.

You can enable it by putting your code in the "checked" section:

  Int64 a = 12345678912345; checked { Console.Write((int)a); } 

You can achieve the same by changing the compiler options.

+3
source

In modern versions of VS (starting with VS 2003 or so?), Arithmetic overflow / underflow check is disabled by default. You can change this in the project properties β†’ build β†’ advanced β†’ "Check arithmetic overflow / underflow".

+1
source

The reason foreach hides that the cast is happening is because it originated in C # 1 - before generics.

It is defined as (section 8.8.4 of the C # v5 language specification ):

A for any form statement

 foreach (V v in x) embedded-statement 

then expand to:

 { E e = ((C)(x)).GetEnumerator(); try { while (e.MoveNext()) { V v = (V)(T)e.Current; embedded-statement } } finally { … // Dispose e } } 

As you can see, foreach automatically gets an explicit selection in this first line.

+1
source

Your value 12345678912345 gets truncated to a 32-bit integer. This is easier to understand in hexadecimal format:

  • 12345678912345 in the database 10 b3a73ce5b59 in hexadecimal format.
  • b3a73ce5b59 with truncated to 32 bits 73ce5b59 (keep the least significant 8 hexadecimal digits).
  • 73ce5b59 in hexadecimal format 1942903641 in the base 10.

Regarding why you are not getting any errors or warnings:

  • Your conversion is not related to a compile-time constant, so the compiler will not perform any static checks (in this simple case, it could, but not at all).
  • By default, converting an explicit C # long conversion to int generates a conv.i4 CIL statement that truncates the value without exception when overflowing. Using the checked statement or expression or compiling with the /checked switch, the C # compiler emits conv.ovf.i4 , which conv.ovf.i4 overflow exception.
0
source

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


All Articles