Is there a nice way to split an int into two short

2020-02-26 14:45发布

I think that this is not possible because Int32 has 1 bit sign and have 31 bit of numeric information and Int16 has 1 bit sign and 15 bit of numeric information and this leads to having 2 bit signs and 30 bits of information.

If this is true then I cannot have one Int32 into two Int16. Is this true?

Thanks in advance.

EXTRA INFORMATION: Using Vb.Net but I think that I can translate without problems a C# answer.

What initially I wanted to do was to convert one UInt32 to two UInt16 as this is for a library that interacts with WORD based machines. Then I realized that Uint is not CLS compliant and tried to do the same with Int32 and Int16.

EVEN WORSE: Doing a = CType(c And &HFFFF, Int16); throws OverflowException. I expected that statement being the same as a = (Int16)(c & 0xffff); (which does not throw an exception).

12条回答
The star\"
2楼-- · 2020-02-26 14:52

Jon's answer, translated into Visual Basic, and without overflow:

Module Module1
    Function MakeSigned(ByVal x As UInt16) As Int16
        Dim juniorBits As Int16 = CType(x And &H7FFF, Int16)
        If x > Int16.MaxValue Then
            Return juniorBits + Int16.MinValue
        End If
        Return juniorBits
    End Function

    Sub Main()
        Dim original As Int32 = &H7FFFFFFF    
        Dim firstHalfUnsigned As UInt16 = CType(original >> 16, UInt16)
        Dim secondHalfUnsigned As UInt16 = CType(original And &HFFFF, UInt16)
        Dim firstHalfSigned As Int16 = MakeSigned(firstHalfUnsigned)
        Dim secondHalfSigned As Int16 = MakeSigned(secondHalfUnsigned)

        Console.WriteLine(firstHalfUnsigned)
        Console.WriteLine(secondHalfUnsigned)
        Console.WriteLine(firstHalfSigned)
        Console.WriteLine(secondHalfSigned)
    End Sub
End Module

Results:

32767
65535
32767
-1

In .NET CType(&Hffff, Int16) causes overflow, and (short)0xffff gives -1 (without overflow). It is because by default C# compiler uses unchecked operations and VB.NET checked.

Personally I like Agg's answer, because my code is more complicated, and Jon's would cause an overflow exception in checked environment.

I also created another answer, based on code of BitConverter class, optimized for this particular task. However, it uses unsafe code.

查看更多
做个烂人
3楼-- · 2020-02-26 14:54

This should work:

int original = ...;
byte[] bytes = BitConverter.GetBytes(original);
short firstHalf = BitConverter.ToInt16(bytes, 0);
short secondHalf = BitConverter.ToInt16(bytes, 2);

EDIT:

tested with 0x7FFFFFFF, it works

byte[] recbytes = new byte[4];
recbytes[0] = BitConverter.GetBytes(firstHalf)[0];
recbytes[1] = BitConverter.GetBytes(firstHalf)[1];
recbytes[2] = BitConverter.GetBytes(secondHalf)[0];
recbytes[3] = BitConverter.GetBytes(secondHalf)[1];
int reconstituted = BitConverter.ToInt32(recbytes, 0);
查看更多
够拽才男人
4楼-- · 2020-02-26 14:55

yes it can be done using masking and bitshifts

 Int16 a,b;
 Int32 c;

 a = (Int16) (c&0xffff);
 b = (Int16) ((c>>16)&0xffff);

EDIT

to answer the comment. Reconstructionworks fine:

 Int16 a, b;
 Int32 c = -1;

 a = (Int16)(c & 0xffff);
 b = (Int16)((c >> 16) & 0xffff);

 Int32 reconst = (((Int32)a)&0xffff) | ((Int32)b << 16);

 Console.WriteLine("reconst = " + reconst);

Tested it and it prints -1 as expected.

EDIT2: changed the reconstruction. The promotion of the Int16 to Int32 caused all sign bits to extend. Forgot that, it had to be AND'ed.

查看更多
地球回转人心会变
5楼-- · 2020-02-26 14:55

Due to storage width (32bits and 16bits), converting Int32 to Int16 may imply a loss of information, if your Int32 is greater than 32767.

查看更多
贼婆χ
6楼-- · 2020-02-26 14:58

You can use StructLayout in VB.NET:

correction: word is 16bit, dword is 32bit

<StructLayout(LayoutKind.Explicit, Size:=4)> _
   Public Structure UDWord
      <FieldOffset(0)> Public Value As UInt32
      <FieldOffset(0)> Public High As UInt16
      <FieldOffset(2)> Public Low As UInt16

      Public Sub New(ByVal value As UInt32)
         Me.Value = value
      End Sub

      Public Sub New(ByVal high as UInt16, ByVal low as UInt16)
         Me.High = high
         Me.Low = low
      End Sub
   End Structure

Signed would be the same just using those types instead

<StructLayout(LayoutKind.Explicit, Size:=4)> _
   Public Structure DWord
      <FieldOffset(0)> Public Value As Int32
      <FieldOffset(0)> Public High As Int16
      <FieldOffset(2)> Public Low As Int16

      Public Sub New(ByVal value As Int32)
         Me.Value = value
      End Sub

      Public Sub New(ByVal high as Int16, ByVal low as Int16)
         Me.High = high
         Me.Low = low
      End Sub
   End Structure

EDIT:

I've kind of rushed the few times I've posted/edited my anwser, and yet to explain this solution, so I feel I have not completed my answer. So I'm going to do so now:

Using the StructLayout as explicit onto a structure requires you to provide the positioning of each field (by byte offset) [StructLayoutAttribute] with the FieldOffset attribute [FieldOffsetAttribute]

With these two attributes in use you can create overlapping fields, aka unions.

The first field (DWord.Value) would be the 32bit integer, with an offset of 0 (zero). To split this 32bit integer you would have two additional fields starting again at the offset of 0 (zero) then the second field 2 more bytes off, because a 16bit (short) integer is 2 bytes a-peice.

From what I recall, usually when you split an integer they normally call the first half "high" then the second half "low"; thus naming my two other fields.

With using a structure like this, you could then create overloads for operators and type widing/narrowing, to easily exchange from say an Int32 type to this DWord structure, aswell as comparasions Operator Overloading in VB.NET

查看更多
Rolldiameter
7楼-- · 2020-02-26 15:02

This can certainly be done with no loss of information. In both cases you end up with 32 bits of information. Whether they're used for sign bits or not is irrelevant:

int original = ...;

short firstHalf = (short) (original >> 16);
short secondHalf = (short) (original & 0xffff);

int reconstituted = (firstHalf << 16) | (secondHalf & 0xffff);

Here, reconstituted will always equal original, hence no information is lost.

Now the meaning of the signs of the two shorts is a different matter - firstHalf will be negative iff original is negative, but secondHalf will be negative if bit 15 (counting 0-31) of original is set, which isn't particularly meaningful in the original form.

查看更多
登录 后发表回答