如何安全地将长整型转换为整型?

4
根据问题 "Can I convert long to int?",在C#中将long转换为int的最佳方法是:
int myIntValue = unchecked((int)myLongValue);

在VB.NET中如何实现这个功能?据我了解,VB.NET没有unchecked的概念。

当我将代码转换为VB.NET时,转换器提示使用CInt()。但是CInt会抛出system.overflowexception异常。在C#中使用unchecked可以避免system.overflowexception异常。

我不想使用异常捕获。

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

Dim y As Integer = CInt(x)
Console.WriteLine(y)

4
你希望 VB.NET 对于一个大于 Int.MaxValue 的 Long 值使用什么值? - D Stanley
1
@Cory:这是C#的默认设置,许多人似乎对此感到满意。个人而言,在大多数情况下我喜欢使用检查过的算术运算... - Jon Skeet
1
@JonSkeet:有趣,我不知道那个。我必须同意你的看法。 - Cᴏʀʏ
检查算术的具体项目设置是什么? - Hoppe
@Hoppe 值是否可以为负数? - Andrew Morton
显示剩余7条评论
6个回答

7

如您在我相关的类似问题的答案中所看到的,有很多方法可以实现这种操作,但没有比简单的Try/Catch块更方便的方法。如果您想避免Try/Catch块的低效率,我建议先检查值是否正确,例如:

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

或者,如果你想在数值超出范围时将其设置为特定的值:

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

如果你想让它更方便,你可以将其制作成一个函数。如果方便是最终目标,甚至可以将其制作成扩展方法:
<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

如果 myLongValue 超出了该范围怎么办? - D Stanley
只需添加一个 Else,然后做任何你想做的事情。 - Steven Doggart
1
你可以使用 AndAlso 来短路你的 If 语句。 - Cᴏʀʏ

1

是否已经编写了用于检查其是否大于该值并将其设置为 Int32.MaxValue 的转换函数?

没有,但这很简单:

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

或者在一行中:

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

或者使用 Math.Min

i = CInt(Math.Max(Math.Min(l, int32.MaxValue), int32.MinValue))

Math.Min版本干净且适用于OP的示例。 - perry

1
这里有一种相对简单的方式将long转换为int,只需提取低32位而不抛出异常。无需进行64位除法,无需try/catch块,只需要进行位操作和一次比较。
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

这里有一个更简单的版本 - 只需进行位操作。完全没有比较。我们只需获取2个16位半字节并将它们组装成最终值:
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

编辑说明:

另一个选项是使用可空整数(System.Nullable<int>)。在VB.Net中,它会像这样:

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

或者,如果有意地捕获和吞咽异常的概念让您感到困扰:
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

无论如何,返回值将是一个整数值,或者如果64位值超出32位整数的范围,则返回 Nothing

我的结果是-2147483549。将x设置为Long.MaxValue,结果为-1... - perry
这是因为它给出了你在原始问题中的C#示例的等效项。像C#中的long x = Long.MaxValue; int y = (int) x ;这样的代码只会给你64位长整型的低32位。由于Long.MaxValue0x7FFFFFFFFFFFFFFF,所以你的结果是0xFFFFFFFF。作为一个有符号的32位整数,这就是-1的表示形式。 - Nicholas Carey
谢谢解释。不太清楚OP想要什么。是“是否已经编写了转换函数,以检查它是否大于该值,如果不是,则将其设置为Int32.maxvalue?”还是“如何安全地将长整型缩小为整型?”......无论如何,我认为他有足够的答案来确定哪个适合问题并继续前进 :) - perry

0
你可以使用 mod 函数和 Convert.ToInt32 来实现:
i = Convert.ToInt32(x Mod (Int32.MaxValue))

接近了,但这只返回100(仅余数)。 - perry
你可以将这个方法和其他答案结合使用,将一个长整型分割成两个整型,因此它仍然很有用。 - perry

0

到目前为止,已经有很多好的答案了,这里再提供一个与Steven Doggart类似的答案:

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)

结果:

2147483747
2147483647

或者:

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)

结果:

9223372036854775807
2147483647

将其封装在一个函数中:

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

请注意函数名称。我将其命名为这样,以表明这将导致截断/数据丢失,不应用于“转换”Long到Int,这可能会有损失,并且这也可能是各种转换函数不尝试执行此操作的原因。只是猜测。

0
一种无分支执行此类操作的方法是:
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

请注意,在每种情况下,最后一个十六进制值是一个值为-2147483648的Int32,因此将其添加到无符号类型中将导致升级为Int64;将其添加到上一个XOR的结果中将始终产生一个在Int32范围内的值。
我希望编译器提供类似于这些中的一个作为编译器内置函数,因为即使操作是无分支的,JIT可能会将所有内容减少到32位的“xor”和32位的“add”指令,但不应该需要使用任何代码来完成这一点。
顺便说一句,我认为另一种方法是定义一个具有“LayoutKind.Explicit”属性的结构,重叠一个Int32和一个UInt32;这可能会消除不必要的相互抵消的算术运算,但它可能会强制JIT将一个值存储到内存中,并在可以保持寄存器中的情况下重新加载它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接