9

I have a project where I want to have checked arithmetic by default, except for one performance sensitive spot. Unfortunately, VB.Net doesn't have an 'unchecked' block.

Ideally the framework would have some sort of integer type with explicitly unchecked arithmetic, but I didn't find anything like that. I did find that expression trees have binary expressions for unchecked operations, but the delegate overhead cancels out the unchecked advantage (and then some).

Currently I'm converting the inputs to UInt64/Int64 before doing the arithmetic, then converting back (with a bitwise And to ensure in-range). It's about 50% slower than unchecked arithmetic (according to profiling).

Moving the arithmetic-sensitive part to a project with unchecked arithmetic might work, but it seems like overkill to give it an assembly all to itself.

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136
  • 1
    An extra C# assembly for this might be overkill, but you can also create a module from the C# file (some command-line parameter for the compiler), and then use ILmerge to merge it with your VB.NET assembly. – OregonGhost Mar 08 '10 at 17:16

3 Answers3

6

I know this is old, but I recently needed to convert some C# code that used unchecked and I thought I'd share how I did it. It's pure VB code and can be scoped however you need (rather than a project-wide option).

The trick is to create a structure that contains a Long field and two Integer fields. Then use StructLayout and FieldOffset attributes to create a union of the long and the two integers. The fields can (should) be private. Use widening CType operators to convert from a Long to the structure and from the structure to an Integer (using the low integer value). Add operator overloads for +, -, *, etc... and presto! unchecked arithmetic in VB!

Sort of... it will still overflow, as Strilanc pointed out, if the long value goes outside the range for longs. But it works pretty well for a lot of situations where unchecked is used.

Here's an example:

<StructLayout(LayoutKind.Explicit)>
Public Structure UncheckedInteger

    <FieldOffset(0)>
    Private longValue As Long
    <FieldOffset(0)>
    Private intValueLo As Integer
    <FieldOffset(4)>
    Private intValueHi As Integer

    Private Sub New(newLongValue As Long)
        longValue = newLongValue
    End Sub

    Public Overloads Shared Widening Operator CType(value As Long) As UncheckedInteger
        Return New UncheckedInteger(value)
    End Operator

    Public Overloads Shared Widening Operator CType(value As UncheckedInteger) As Long
        Return value.longValue
    End Operator

    Public Overloads Shared Widening Operator CType(value As UncheckedInteger) As Integer
        Return value.intValueLo
    End Operator

    Public Overloads Shared Operator *(x As UncheckedInteger, y As Integer) As UncheckedInteger
        Return New UncheckedInteger(x.longValue * y)
    End Operator

    Public Overloads Shared Operator Xor(x As UncheckedInteger, y As Integer) As UncheckedInteger
        Return New UncheckedInteger(x.longValue Xor y)
    End Operator

    ' Any other operator overload you need...
End Structure

Use the structure in code like this:

Dim x As UncheckedInteger = 2147483647
Dim result As Integer = x * 2  ' This would throw OverflowException using just Integers

Console.WriteLine(result.ToString())  ' -2

Careful that your calculations don't overflow before assigning the result to an UncheckedInteger. You could create UncheckedShort and UncheckedByte structures using the same technique.

Tim Overbay
  • 61
  • 1
  • 2
  • Won't this still eventually overflow? What happens if you multiply by two until the range of the long is exceeded as well? – Craig Gidney Mar 27 '13 at 01:33
  • The code as I posted it would certainly overflow if the long value went outside it's range. In my situation, I didn't need to worry about that. If overflowing the long value is a concern, one could always add range checks. – Tim Overbay Mar 27 '13 at 16:21
2

Personally, I think leaving this in its own assembly, especially since it'll be such a small assembly, is a good option. This makes maintenance easier, since it's easy to regenerate this assembly at any time. Just make a separate assembly flagged unchecked, and put your performance-sensitive code there.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • You can do unchecked arithmetic in VB. The problem is that it's a project-wide option (under Advanced Compile Options in the Compile tab). – Craig Gidney Mar 08 '10 at 17:28
  • @Strilanc: Even better - edited my answer, then. I'd just make a separate project for your perf. critical code, and reference it, then. – Reed Copsey Mar 08 '10 at 17:51
  • 4
    I did something slightly different: I wrote types with unchecked arithmetic and put those in the unchecked assembly. Now I have ModByte/ModInt16/ModInt32/ModInt64 and the unchecked assembly definitely won't slowly absorb code. – Craig Gidney Mar 08 '10 at 19:04
0

I've recently created a nuget package for unchecked integer operations in VB.NET, see nuget package VBMath.Unchecked or Github repository System.Runtime.Extensions.Unchecked.

Using System.Numerics

' this will not throw
Dim value = UncheckedInteger.Add(Integer.MaxValue, 1)
Markus Hartmair
  • 672
  • 7
  • 15