如何无符号整数转换成有符号整数,而不发生OverflowException(How to conve

2019-07-19 07:45发布

我想是能够转换一个高价值的无符号整数有符号整数(即使用最高位的值)。 在这种情况下,我不关心该值大于符号整数型的最大值。 我只是希望它转换到任何位值代表作为签署整数。 换句话说,我希望它导致一个负数。

然而,VB.NET,该CType操作不工作的方式(或任何类似的其他转换功能CShortCInteger )。 当您尝试转换一个无符号的值比期望的签署型的最高值时,它抛出一个OverflowException ,而不是返回一个负数。 例如:

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

值得一提的,那就是,在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'

我已经想通了,做我想做什么的一种方式,但似乎不必要的丑陋。 下面是我如何开始工作:

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

就像我说的,这样的作品,但如果有在VB.NET做的更简单,更清洁的方式,我很想知道它是什么。

Answer 1:

经常使用的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;

<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


Answer 2:

早在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

至少在.NET中,您可以创建一个扩展方法出这使它更好过



Answer 3:

非常简单:

对于32位

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

val32结束= -1294967296

对于16位

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

val16结束= -5536



Answer 4:

我发现这一点: ??在VB.NET类型转换的问题?

关于网页下半部是这样的:

老,VB“适当”出十六进制“侧步”的把戏,然后再返回仍然有效!

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

看来工作非常漂亮!



Answer 5:

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

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


Answer 6:

我只是碰到这样的问题,也是和现在看来似乎不是很优化,并没有像BitConverter方法。 所以,我认为,在内存中的数据的存储是真的既为int和uint仅有4个字节。

下面似乎是最有效的方式来处理这一点,并在可使用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

希望这可以帮助别人。



Answer 7:

通常情况下,这将与在高级语言流来完成,但是Net框架暴露的方式,而不使用元帅中间流对象这样做。

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

令我惊讶的是,使用元帅似乎比使用运算更高效的基础上,我得到了统计数据

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

从测试:

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

更奇怪的是,少帅做法似乎可以忽略不计的C#样式转换为有效的。 在我第一次运行时,元帅的做法是比较慢,但在第二轮,元帅的做法是快。 这是第二次运行的结果

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

使用此代码

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();
    }
}

我们带来的王。

// 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;
}

好了,结果是非常有趣

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


Answer 8:

Necromancing。
作为补充,马克Gravell的答案,如果你想知道如何做到这一点的头部:

你可以把它写一般为:

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

由于类型检查的,代码是这样的:

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;
}

然后,如果你想这样做的头,你可以做这样的:

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


Answer 9:

在下面这个例子中,由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

从概念上讲,它是由“转换”类型的可变的不同。 更确切地说,该方法显示出存储的实体。 在访问它各部分的同时,不同的方式提供。

由于该操作是“访问”不“转化”,这是非常快的,精干高效的(见成员对马克的发表评论)。

字节序是由编译器处理。



Answer 10:

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

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

这使得签约,无符号的转换非常简单:

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


Answer 11:

不知道VB,但我希望它是类似于C#,因为它是.NET代码。 在C#中,你可以简单地使用类型转换:

UInt16 ui = 65000;
Int16   i = (Int16)ui;

完成。



文章来源: How to convert unsigned integer to signed integer without OverflowException