Default con­text = unchecked

By default, arith­metic oper­a­tions and con­ver­sions in C# are exe­cuted in an unchecked con­text. This means that for a signed inte­ger it over­flows from int.MaxValue to int.MinValue and under­flows from int.MinValue to int.MaxValue, hence both state­ments below eval­u­ates to true:

(int.MinValue – 1) == int.MaxValue;
(int.MaxValue + 1) == int.MinValue;

Sim­i­larly, for an unsigned inte­ger it will under­flow from 0 to uint.MaxValue and over­flow from uint.MaxValue to 0:

(0 – 1) == uint.MaxValue; // uint.MinValue = 0
(uint.MaxValue + 1) == 0;

This also applies to con­ver­sions too:

(int)((long) int.MaxValue + 1) == int.MinValue; // true

Pit­falls

This default behav­iour of swal­low­ing the excep­tions seems rather strange and unex­pected to me, given that when an over­flow hap­pens it’s usu­ally of inter­est to me as a devel­oper to know about it and deal with it as appro­pri­ate because:

  • if an over­flow is to hap­pen dur­ing nor­mal usage of the appli­ca­tion it prob­a­bly means there’s some­thing wrong with the design/assumptions of the applicatoin
  • if an over­flow is to hap­pen and the appli­ca­tion is allowed to con­tinue and per­sist the overflowed/underflowed value it could mean impor­tant pieces of data are left in an invalid state which is difficult/impossible to revert
  • the jump is large! Imag­ine see­ing your account bal­ance go from £2147483647 to £–2147483648 after cred­it­ing £1 into it..

With that said, unchecked con­text per­forms sig­nif­i­cantly bet­ter than checked imple­men­ta­tion, which is prob­a­bly why it was cho­sen as the default.

Also, you will prob­a­bly want to use unchecked blocks for cal­cu­lat­ing hash codes where it’s the bit pat­terns that mat­ters not the mag­ni­tude of the inte­ger value, i.e.:

public class MyClass
{
    …
    public override int GetHashCode()
    {
        unchecked
        {
            return ….
        }
    }
}

Scope

There are two things to keep in mind when using checked/unchecked blocks:

They’re always LOCAL to the method

Which means if you call another method from within a checked block the method will still exe­cute in the default con­text (unchecked):

checked
{
    // this method will still execute in unchecked context
    DoSomethingThatOverflows();
}
...
public void DoSomethingThatOverflows()
{
    // no overflow exception is thrown...
    var overflowed = int.MaxValue + 1;
}

The con­text a line of code exe­cutes in is deter­mined by the MOST inner checked/unchecked state­ment block

Hence the fol­low­ing code will exe­cute in unchecked context:

checked
{
    unchecked
    {
        var over = int.MaxValue + 1;
    }
}

Project-wide arith­metic overflow/underflow checks

If you require arith­metic overflow/underflow check­ing on a project-wide scale, there is a prop­erty you can set from within Visual Stu­dio. Go to project prop­er­ties and find the Build tab, click “Advanced…” and tick the “Check for arith­metic overflow/underflow” box. But remem­ber, you would prob­a­bly still want to make sure GetH­ash­Code is exe­cuted in an unchecked context.

Share

4 Responses to “Understanding arithmetic overflow checking in C#”

  1. found your site on del.icio.us today and really liked it.. i book­marked it and will be back to check it out some more later

  2. […] learn more on unchecked and checked, please refer to this post or that arti­cle on Code […]

  3. […] the scope of the checked con­text is local to the method – i.e. if in your checked block you call another method then that method is not […]

  4. samour says:

    really inter­sted

Leave a Reply