How to convert unsigned integer to signed integer

2019-02-16 12:43发布

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 CType operation doesn't work that way (or any of the other conversion functions like CShort andCInteger). When you try to convert an unsigned value that is higher than the desired signed-type's maximum value, it throws an OverflowException rather than returning a negative number. For instance:

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

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

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.

10条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-16 13:09

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

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

查看更多
我想做一个坏孩纸
3楼-- · 2019-02-16 13:10

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.

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

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.

查看更多
ら.Afraid
4楼-- · 2019-02-16 13:10

Don't know VB, but I expect it is similar to C# as it is .NET code. In C# you can simply use type cast:

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

Done.

查看更多
smile是对你的礼貌
5楼-- · 2019-02-16 13:13

If you need to do this often, you can create 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
Dim y As Short = x.ToSigned
查看更多
贪生不怕死
6楼-- · 2019-02-16 13:14

Very simple:

For 32 bit

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

val32 ends up = -1294967296

For 16 bit

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

val16 ends up = -5536

查看更多
时光不老,我们不散
7楼-- · 2019-02-16 13:19

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

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
查看更多
登录 后发表回答