vb.net 如何在没有OverflowException的情况下将无符号整数转换为有符号整数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14708778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 12:12:35  来源:igfitidea点击:

How to convert unsigned integer to signed integer without OverflowException

.netvb.netcastingbitconverteroverflowexception

提问by Steven Doggart

I would like to be able to convert a high-valued unsigned-integer (a value that uses the highest-order bit) to a signed-integer. In this case, I don't care that the value is higher than the maximum value of the signed integer type. I just want it to convert to whatever the bit-values represent as a signed-integer. In other words, I would expect it to result in a negative number.

我希望能够将高值无符号整数(使用最高位的值)转换为有符号整数。在这种情况下,我不在乎该值是否高于有符号整数类型的最大值。我只是希望它转换为任何位值表示为有符号整数。换句话说,我希望它会导致负数。

However, with VB.NET, the CTypeoperation doesn't work that way (or any of the other conversion functions like CShortandCInteger). When you try to convert an unsigned value that is higher than the desired signed-type's maximum value, it throws an OverflowExceptionrather than returning a negative number. For instance:

但是,对于 VB.NET,该CType操作不能那样工作(或任何其他转换函数,如CShortCInteger)。当您尝试转换高于所需有符号类型最大值的无符号值时,它会抛出一个OverflowException而不是返回一个负数。例如:

Dim x As UShort = UShort.MaxValue
Dim y As Short = CShort(x)  ' Throws OverflowException

It's worth mentioning, too, that the DirectCastoperation cannot be used to cast the value between the signed and unsigned types, since neither type inherits or implements the other. For instance:

同样值得一提的是,该DirectCast操作不能用于在有符号和无符号类型之间转换值,因为这两种类型都不会继承或实现另一个。例如:

Dim x As UShort = UShort.MaxValue
Dim y As Short = DirectCast(x, Short)  ' Won't compile: "Value of type 'UShort' cannot be converted to 'Short'

I have figured out one way to do what I want, but it seems unnecessarily ugly. Here's how I got it to work:

我想出了一种方法来做我想做的事,但它似乎不必要地丑陋。这是我如何让它工作的:

Dim x As UShort = UShort.MaxValue
Dim y As Short = BitConverter.ToInt16(BitConverter.GetBytes(x), 0)  ' y gets set to -1

Like I said, that works, but if there's an easier, cleaner way of doing it in VB.NET, I'd love to know what it is.

就像我说的那样,这可行,但如果在 VB.NET 中有更简单、更简洁的方法,我很想知道它是什么。

回答by Marc Gravell

Constant use of BitConverteris going to be a bit inconvenient if you are using that a lot - in particular for performance. If that was me, I would be sorely tempted to add a utilities library in C# that can do direct conversions (via unchecked, although uncheckedis normally the default in C# anyway), and reference that library for this. Another option might be to abuse a "union" struct; the following should translate to VB fairly easily:

经常使用的BitConverter将是一个有点不方便,如果你使用的是有很多-尤其是对于性能。如果是我,我会非常想添加在C#中,可以做直接转换一个实用程序库(通过unchecked,但unchecked通常是在C#中默认反正),并参考该库这一点。另一种选择可能是滥用“联合”结构;以下内容应该很容易转换为 VB:

[StructLayout(LayoutKind.Explicit)]
struct EvilUnion
{
    [FieldOffset(0)] public int Int32;
    [FieldOffset(0)] public uint UInt32;
}
...
var evil = new EvilUnion();
evil.Int32 = -123;
var converted = evil.UInt32;

i.e.

IE

<System.Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Explicit)>
Structure EvilUnion
    <System.Runtime.InteropServices.FieldOffset(0)>
    Public Int32 As Integer
    <System.Runtime.InteropServices.FieldOffset(0)>
    Public UInt32 As UInteger
End Structure
...
Dim evil As New EvilUnion
evil.Int32 = -123
Dim converted = evil.UInt32

回答by tcarvin

Back in the VB6 days we had to write routines like this all the time:

回到 VB6 时代,我们不得不一直编写这样的例程:

Private Function ToShort(ByVal us As UShort) As Short
   If (us And &H8000) = 0 Then
      Return CType(us, Short)
   Else
      Return CType(CType(us, Integer) - UShort.MaxValue - 1, Short)
   End If
End Function

At least in .NET you can create an extension method out of this to make it nicer through

至少在 .NET 中,您可以从中创建一个扩展方法,以使其更好

回答by Alberto58

I think the easiest way is as follows:

我认为最简单的方法如下:

Public Function PutSign(ByVal number As UShort) As Short
    If number > 32768 Then 'negative number
        Return (65536 - number) * -1
    Else
        Return number
    End If
End Function

回答by JerryCic

Very simple:

很简单:

For 32 bit

对于 32 位

    Dim uVal32 As UInt32 = 3000000000
    Dim Val32 As Int32 = Convert.ToInt32(uVal32.ToString("X8"), 16)

val32 ends up = -1294967296

val32 结束 = -1294967296

For 16 bit

对于 16 位

    Dim uVal16 As UInt16 = 60000
    Dim Val16 As Int16 = Convert.ToInt16(uVal16.ToString("X4"), 16)

val16 ends up = -5536

val16 结束 = -5536

回答by user3033282

I found this: ??problems typecasting in VB.NET??

我发现了这个:?? VB.NET 中的类型转换问题??

About halfway down the page is this:

大约在页面的中间是这样的:

The old, VB "Proper" trick of "side-stepping" out to Hexadecimal and back again still works!

旧的 VB“正确”技巧“侧步”到十六进制并再次返回仍然有效!

Dim unsigned as UInt16 = 40000
Dim signed as Int16 = CShort(Val("&H" & Hex(unsigned)))

It seems to work pretty slick!

它似乎工作得很好!

回答by iQueue

I was just faced with this issue as well and didn't like the BitConverter approach as it seems like it's not very optimized. So, I considered that the storage of the data in memory is really just 4 bytes for both an int and uint.

我也刚遇到这个问题,不喜欢 BitConverter 方法,因为它似乎不是很优化。所以,我认为内存中的数据存储对于 int 和 uint 来说实际上只有 4 个字节。

The following seems to be the most efficient way to handle this and works in all .NET languages that can use the Marshal class...

以下似乎是处理此问题的最有效方法,并且适用于所有可以使用 Marshal 类的 .NET 语言......

Dim x as UInteger = &H87654321
Dim gch as GCHandle = GCHandle.Alloc(x, Pinned)
Dim y as Integer = Marshal.ReadInt32(gch.AddrOfPinnedObject)
gch.Free

Hope this helps someone.

希望这可以帮助某人。

回答by Dmitry

Normally, this would be done with streams in higher level languages, but .Net framework exposes a way to do so without intermediate stream objects using Marshal.

通常,这将使用高级语言中的流来完成,但 .Net 框架公开了一种无需使用 Marshal 的中间流对象即可完成此操作的方法。

Imports System.Runtime.InteropServices
Module Module1
    Sub Main()
        Dim given As Int16 = -20
        Dim buffer As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(given))
        Marshal.StructureToPtr(given, buffer, False)
        Dim result As UInt16 = Marshal.PtrToStructure(buffer, GetType(UInt16))
        MsgBox(result)
    End Sub
End Module

To my surprise, Using Marshal seems to be more efficient than using Math, based on the stats I got

令我惊讶的是,根据我得到的统计数据,使用 Marshal 似乎比使用 Math 更有效

4 seconds of v1 yielded: 2358173 conversions
4 seconds of v2 yielded: 4069878 conversions

from test:

来自测试:

Imports System.Runtime.InteropServices

Module Module1
    Function v1(given As Int16) As UInt16
        Dim buffer As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(given))
        Marshal.StructureToPtr(given, buffer, False)
        Dim result As UInt16 = Marshal.PtrToStructure(buffer, GetType(UInt16))
        v1 = result
    End Function

    Function v2(given As Int16) As UInt16
        If given < 0 Then
            given = (Not given) + 1
        End If
        v2 = given
    End Function


    Sub Main()
        Dim total0 As Integer
        Dim total1 As Integer
        Dim t0 As DateTime = DateTime.Now()
        While ((DateTime.Now() - t0).TotalSeconds() < 4)
            v1(-Rnd() * Int16.MaxValue)
            total0 = total0 + 1
        End While

        Console.WriteLine("4 seconds of v1 yielded: " & total0 & " conversions")
        t0 = DateTime.Now()
        While ((DateTime.Now() - t0).TotalSeconds() < 4)
            v2(-Rnd() * Int16.MaxValue)
            total1 = total1 + 1
        End While
        Console.WriteLine("4 seconds of v2 yielded: " & total1 & " conversions")

        Console.ReadKey()
    End Sub

End Module

Stranger still, Marshal approach seems negligibly as effective as C# style cast. On my first run, marshal approach was slower, but on second run, marshal approach was faster. This is the result of the second run

更奇怪的是,Marshal 方法与 C# 样式转换的效果似乎可以忽略不计。在我第一次运行时,元帅进场速度较慢,但​​在第二次运行时,元帅进场速度更快。这是第二次运行的结果

4 seconds of v1 yielded: 1503403 conversions
4 seconds of v2 yielded: 1240585 conversions
4 seconds of v3 yielded: 1592731 conversions

using this code

使用此代码

using System;
using System.Runtime.InteropServices;

class Program
{
    static DateTime startTime = DateTime.Now;        

    static double time {
        get {
            return (DateTime.Now - startTime).TotalMilliseconds;
        }
    }
    static ushort v1(short given) {
        if (given > 0) {
            return (ushort)given;
        }
        return (ushort)(~given + 1);
    }    

    static ushort v2(short given) {
        var buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(given));
        Marshal.StructureToPtr(given, buffer, false);
        ushort result = (ushort)Marshal.PtrToStructure(buffer, typeof(ushort));
        return result;
    }

    static ushort v3(short given)
    {
        return (ushort)given;
    }

    static void Main(string[] args)
    {
        int total0 = 0;
        int total1 = 0;
        int total2 = 0;
        double t0;

        t0 = time;
        while (time - t0 < 4000) {
            v1((short)(-new Random().NextDouble() * Int16.MaxValue));
            ++total0;
        }

        Console.WriteLine("4 seconds of v1 yielded: " + total0 + " conversions");

        t0 = time;
        while (time - t0 < 4000) {
            v2((short)(-new Random().NextDouble() * Int16.MaxValue));
            ++total1;
        }
        Console.WriteLine("4 seconds of v2 yielded: " + total1 + " conversions");


        t0 = time;
        while (time - t0 < 4000) {
            v3((short)(-new Random().NextDouble() * Int16.MaxValue));
            ++total2;
        }
        Console.WriteLine("4 seconds of v3 yielded: " + total2 + " conversions");


        Console.ReadKey();
    }
}

Now to bring in the king;

现在把国王带进来;

// ConsoleApplication3.cpp : main project file.

#include "stdafx.h"

using namespace System;
using namespace System::Runtime::InteropServices;

unsigned __int16 v4(__int16 given) {
    return (unsigned __int16)given;
}

public ref class Program
{
public:
    static DateTime startTime = DateTime::Now;

    static property double time {
        double get() {
            return (DateTime::Now - startTime).TotalMilliseconds;
        }
    }

    static UInt16 v1(Int16 given) {
        if (given > 0) {
            return given;
        }
        return (UInt16)(~given + 1);
    }    

    static UInt16 v2(Int16 given) {
        IntPtr buffer = Marshal::AllocCoTaskMem(Marshal::SizeOf(given));
        Marshal::StructureToPtr(given, buffer, false);
        Type ^t = UInt16::typeid;
        UInt16 result = (UInt16)Marshal::PtrToStructure(buffer, t);
        return result;
    }

    static UInt16 v3(Int16 given)
    {
        return (UInt16)given;
    }

    typedef String ^string;
    static void _Main(array<string> ^args)
    {
        int total0 = 0;
        int total1 = 0;
        int total2 = 0;
        int total3 = 0;
        double t0;

        t0 = time;
        while (time - t0 < 4000) {
            Double d = (gcnew Random())->NextDouble();
            v1((short)(-d * Int16::MaxValue));
            ++total0;
        }

        Console::WriteLine("4 seconds of v1 yielded: " + total0 + " conversions");

        t0 = time;
        while (time - t0 < 4000) {
            v2((short)(-((gcnew Random())->NextDouble()) * Int16::MaxValue));
            ++total1;
        }
        Console::WriteLine("4 seconds of v2 yielded: " + total1 + " conversions");


        t0 = time;
        while (time - t0 < 4000) {
            v3((short)(-((gcnew Random())->NextDouble()) * Int16::MaxValue));
            ++total2;
        }
        Console::WriteLine("4 seconds of v3 yielded: " + total2 + " conversions");

        t0 = time;
        while (time - t0 < 4000) {
            v4((short)(-((gcnew Random())->NextDouble()) * Int16::MaxValue));
            ++total3;
        }
        Console::WriteLine("4 seconds of v4 yielded: " + total3 + " conversions");


        Console::ReadKey();
    }
};


int main(array<System::String ^> ^args)
{
    Program::_Main(args);
    return 0;
}

well, the results are pretty interesting

嗯,结果很有趣

4 seconds of v1 yielded: 1417901 conversions
4 seconds of v2 yielded: 967417 conversions
4 seconds of v3 yielded: 1624141 conversions
4 seconds of v4 yielded: 1627827 conversions

回答by Lambtron

If you need to do this often, you can create high-performance extension methods like these:

如果您需要经常这样做,您可以创建如下的高性能扩展方法:

Imports System.Runtime.CompilerServices

Module SignConversionExtensions

    <StructLayout(LayoutKind.Explicit)> _
    Private Structure Union
        <FieldOffset(0)> Public Int16 As Int16
        <FieldOffset(0)> Public UInt16 As UInt16
    End Structure

    <Extension()> Public Function ToSigned(ByVal n As UInt16) As Int16
        Return New Union() With {.UInt16 = n}.Int16
    End Function

    <Extension()> Public Function ToUnsigned(ByVal n As Int16) As UInt16
        Return New Union() With {.Int16 = n}.UInt16
    End Function

End Module

This makes signed-unsigned conversions very simple:

这使得有符号无符号转换非常简单:

Dim x As UShort = UShort.MaxValue  ' unsigned x = 0xFFFF (65535)
Dim y As Short = x.ToSigned        ' signed y = 0xFFFF (-1)

回答by coarist

In this example below, the answer by Marc Gravell is extended to demonstrate usefulness in VB:

在下面的这个例子中,Marc Gravell 的答案被扩展以展示在 VB 中的用处:

<System.Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Explicit)>
Structure vbUnion16
    <System.Runtime.InteropServices.FieldOffset(0)>
    Public UnSigned16 As UInt16
    <System.Runtime.InteropServices.FieldOffset(0)>
    Public Signed16 As Int16
    <System.Runtime.InteropServices.FieldOffset(0)>
    Public High8 As Byte
    <System.Runtime.InteropServices.FieldOffset(1)>
    Public Low8 As Byte
End Structure

Conceptually, it is different from "converting" types of variable. Rather, the method demonstrated storing an entity. At the same time different ways of access to various parts in it are made available.

从概念上讲,它不同于“转换”类型的变量。相反,该方法演示了存储实体。同时,还提供了访问其中各个部分的不同方式。

Since the operation is "accessing" not "converting", it is very fast, lean and efficient (see member's comments on Marc's post).

由于操作是“访问”而不是“转换”,因此它非常快速、精简和高效(参见成员对 Marc 帖子的评论)。

Endianess is handled by the compiler.

Endianess 由编译器处理。

回答by Stefan Steiger

Necromancing.
As a complement to Marc Gravell's answer, if you wonder how to do it in the head:

死灵法术。
作为对 Marc Gravell 回答的补充,如果您想知道如何在头脑中做到这一点:

You can generally write it as:

您通常可以将其写为:

<unsigned_type> value = unchecked(<unsigned_type>.MaxValue + your_minus_value + 1);

Because of type-checking, code goes like this:

由于类型检查,代码如下:

public uint int2uint(int a)
{
    int sign = Math.Sign(a);
    uint val = (uint) Math.Abs(a);

    uint unsignedValue;
    if(sign > 0) // +a
        unsignedValue = unchecked(UInt32.MaxValue + val + 1);
    else // -a, a=0
        unsignedValue = unchecked(UInt32.MaxValue - val + 1);

    return unsignedValue;
}

And then, if you want to do it in the head, you can do it like this:

然后,如果你想在头脑中做到这一点,你可以这样做:

BigInt mentalResult= <unsigned_type>.MaxValue + your_value;
mentalResult = mentalResult % <unsigned_type>.MaxValue;
if (your_value < 0) // your_value is a minus value
    mentalResult++;

// mentalResult is now the value you search