4

According to the question, "Can I convert long to int?", the best way in C# to convert a long to an int is:

int myIntValue = unchecked((int)myLongValue);

How can I do this in VB.NET? VB.NET does not have the concept of unchecked to my understanding.

When I convert the code in a converter, it says to use CInt(). But CInt throws a system.overflowexception. Using unchecked in C# prevents a system.overflowexception.

I'd rather not catch the exception.

Dim x As Long = Int32.MaxValue
x = x + 100
Console.WriteLine(x)

Dim y As Integer = CInt(x)
Console.WriteLine(y)
Community
  • 1
  • 1
Hoppe
  • 6,508
  • 17
  • 60
  • 114
  • 4
    What value do you want VB.NET to use for a Long value greater then `Int.MaxValue`? – D Stanley Nov 05 '14 at 18:57
  • VB.NET does, but you have to compile your entire assembly that way. Probably not a smart idea. – Cᴏʀʏ Nov 05 '14 at 18:59
  • 1
    @Cory: That's the default in C# - many people seem to be fine with it. Personally I liked checked arithmetic in most cases... – Jon Skeet Nov 05 '14 at 19:01
  • 1
    @JonSkeet: Interesting, I didn't know that. I would have to agree with you. – Cᴏʀʏ Nov 05 '14 at 19:03
  • Good point... I guess just Int32.MaxValue. Is there a conversion function already written that will check if its greater than that, and if not, set it to Int32.maxvalue? @DStanley – Hoppe Nov 05 '14 at 19:04
  • What is the specific project setting on checked arithmetic? – Hoppe Nov 05 '14 at 19:05
  • @Hoppe: What I was referring to is in Visual Studio, under the project's properties pages (`Properties > Compile > Advanced Compile Options`) there's a checkbox for `Remove integer overflow checks`. – Cᴏʀʏ Nov 05 '14 at 19:07
  • @Hoppe Could the value to be converted be negative? – Andrew Morton Nov 05 '14 at 19:18
  • I don't want the value's sign to change. That seems like it is a problem with the answer I linked to @AndrewMorton – Hoppe Nov 05 '14 at 19:56
  • @Hoppe If you don't really care about the data held in the upper bits of an Int64, could you adjust the earlier code to use an Int32 instead so that the problem does not exist? – Andrew Morton Nov 05 '14 at 20:03
  • I don't own that code, so I can't change it unfortunately – Hoppe Nov 05 '14 at 20:05
  • @JimMischel: I got around to testing that after commenting and realized the same thing, hence I did not post an answer :) Going to delete the comment since it serves no purpose. – perry Nov 05 '14 at 20:07

6 Answers6

7

As you can see in the answers to my related, similar question, there are ways to do that sort of thing, but nothing really more convenient than a simple Try/Catch block. If you want to avoid the inefficiency of a Try/Catch block, I would recommend checking the value first, like this:

If (myLongValue >= Integer.MinValue) AndAlso (myLongValue <= Integer.MaxValue) Then
    myIntValue = CInt(myLongValue)
End If

Or, if you want to set the value to something specific when it is outside of the range:

If myLongValue < Integer.MinValue Then
    myIntValue = Integer.MinValue
ElseIf myLongValue > Integer.MaxValue Then
    myIntValue = Integer.MaxValue
Else
    myIntValue = CInt(myLongValue)
End If

If you wanted to make it more convenient, you could make it a function. If convenience is the ultimate goal, you could even make it an extension method:

<Extension>
Public Function AsInt32(value As Long) As Integer
    If value < Integer.MinValue Then
        Return Integer.MinValue
    ElseIf value > Integer.MaxValue Then
        Return Integer.MaxValue
    Else
        Return CInt(value)
    End If
End Function
Community
  • 1
  • 1
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
1

Is there a conversion function already written that will check if its greater than that, and if not, set it to Int32.MaxValue?

No but that would be straightforward:

Dim l as Long = Int32.MaxValue + 12433243243

Dim i as Int32
if l > int32.MaxValue Then
   i = int32.MaxValue
elseIf l < int32.MinValue
   i = int32.MinValue
Else
   i = CInt(l)
End If

Or in a single line:

i = if(l > int32.MaxValue, int32.MaxValue, if(l < int32.MinValue, int32.MinValue, CInt(l)))

Or using Math.Min:

i = CInt(Math.Max(Math.Min(l, int32.MaxValue), int32.MinValue))
D Stanley
  • 149,601
  • 11
  • 178
  • 240
1

Here's a fairly painless way to down cast a long to an int, just extracting the low-order 32 bits without throwing an exception. No 64-bit division, no try/catch blocks, just bit twiddling and a single comparison.

Private Function CastLongToInt(x As Long) As Integer
    Dim value As Integer = CType(x And &H7FFFFFFFL, Integer) ' get the low order 31 bits

    ' If bit 32 (the sign bit for a 32-bit signed integer is set, we OR it in
    If (0 <> (x And &H80000000L)) Then
        value = value Or &H80000000I
    End If

    Return value
End Function

Here's an ever simpler rendition — just bit-twiddling. No no comparisions at all. We just grab 2 16-bit nibbles and assemble them into the final value:

Private Function CastLongToInt(x As Long) As Integer
    Dim hiNibble As Integer = &HFFFFL And (x >> 16) 
    Dim loNibble As Integer = &HFFFFL And x
    Dim value As Integer = (hiNibble << 16) Or loNibble
    Return value
End Function

Edited To Note:

Another option would be to use a nullable integer (System.Nullable<int>). In VB.Net, it would be something like this:

Private Function TryCastLongToInt(x As Long) As Integer?
    Dim value As Integer?
    Try
        value = CType(x, Integer)
    Catch
      ' This exception intentionall swallowed here
    End Try
    Return value
End Function

Or, if the notion of intentionally catching and swallowing exceptions bugs you:

Private Function TryCastLongToInt(x As Long) As Integer?
    If (x < Integer.MinValue) Then Return Nothing
    If (x > Integer.MaxValue) Then Return Nothing
    Return x
End Function

Either way, the return value will be either an integer value or Nothing if the 64-bit value was outside the domain of a 32- bit integer.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • My result for the both of these was -2147483549. Set x to Long.MaxValue and the result is -1... – perry Nov 05 '14 at 21:14
  • That's because this is giving you the equivalent of your C# example in your original question. Code like this in C#'s `long x = Long.MaxValue; int y = (int) x ;` simply gives you the low-order 32 bits of the 64-bit long. Since `Long.MaxValue` is `0x7FFFFFFFFFFFFFFF`, your result is `0xFFFFFFFF`. As a signed 32-bit integer, that's the representation of -1. – Nicholas Carey Nov 05 '14 at 21:30
  • Thanks for explaining. It's unclear exactly what the OP wants. "Is there a conversion function already written that will check if its greater than that, and if not, set it to Int32.maxvalue?" or is it "How can I safely downsize a long to an int?" ... anyhow I think he has enough answers to determine which one fits the problem and carry right along :) – perry Nov 05 '14 at 21:42
0

You can use the mod function with Convert.ToInt32:

i = Convert.ToInt32(x Mod (Int32.MaxValue))
xpda
  • 15,585
  • 8
  • 51
  • 82
0

A branchless way of performing this kind of operation would be:

Function UInt64ToInt32Wrap(v As UInt64) As Int32
    Return CInt((CUInt(v And &HFFFFFFFFUI) Xor &H80000000UI) + &H80000000)
End Function
Function Int64ToInt32Wrap(v As Int64) As Int32
    Return CInt(((v And &HFFFFFFFFL) Xor &H80000000L) + &H80000000)
End Function
Function UInt32ToInt32Wrap(v As UInt32) As Int32
    Return CInt((v Xor &H80000000UI) + &H80000000)
End Function

Note that in each case, the last hex value is an Int32 with value -2147483648, so adding it to an unsigned type will cause a promotion to Int64; adding that to the result of the previous XOR will always yield a value that is in range for an Int32.

I wish something like one of these had been provided as a compiler intrinsic, since even though the operations are branchless and the JIT can probably reduce everything to a 32-bit "xor" and a 32-bit "add" instruction, it shouldn't be necessary to use ANY code to accomplish that.

Incidentally, I think another approach would be to define a structure with a "LayoutKind.Explicit" attribute that overlaid an Int32 and a UInt32; that would probably eliminate the unnecessary mutually-cancelling arithmetic operations, but it might force the jitter to store a value to memory and reload it when it could otherwise keep things in registers.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

Great answers so far, here's another one similar to Steven Doggart's:

Dim x As Long = Int32.MaxValue
x = x + 100
Console.WriteLine(x)

Dim y As Integer
If x > Int32.MaxValue Then y = Int32.MaxValue Else y = x
Console.WriteLine(y)

Results:

2147483747
2147483647

Or:

Dim x As Long = Long.MaxValue
Console.WriteLine(x)

Dim y As Integer
If x > Int32.MaxValue Then y = Int32.MaxValue Else y = x
Console.WriteLine(y)

Results:

9223372036854775807
2147483647

Wrap it in a function:

Function DownsizeLongToInt(ByVal x As Long) As Integer
    Dim y As Integer
    If x > Int32.MaxValue Then y = Int32.MaxValue Else y = x
    Return y
End Function

Note the function name. I've named it that way to indicate that this is going to cause truncation/data loss and shouldn't be used for "converting" a Long to Int, which may be lossy, and which may also be the reason why the various convert functions do not attempt to do this. Just a guess.

perry
  • 266
  • 1
  • 6