Is there a nice way to split an int into two shorts (.NET)?

Viewed 20587

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).

13 Answers

Unsafe code in C#, overflow doesn't occur, detects endianness automatically:

using System;
class Program
{
    static void Main(String[] args)
    {
        checked // Yes, it works without overflow!
        {
            Int32 original = Int32.MaxValue;
            Int16[] result = GetShorts(original);
            Console.WriteLine("Original int: {0:x}", original);
            Console.WriteLine("Senior Int16: {0:x}", result[1]);
            Console.WriteLine("Junior Int16: {0:x}", result[0]);
            Console.ReadKey();
        }
    }
    static unsafe Int16[] GetShorts(Int32 value)
    {
        byte[] buffer = new byte[4];
        fixed (byte* numRef = buffer)
        {
            *((Int32*)numRef) = value;
            if (BitConverter.IsLittleEndian)
                return new Int16[] { *((Int16*)numRef), *((Int16*)numRef + 1) };
            return new Int16[] { 
                (Int16)((numRef[0] << 8) | numRef[1]),  
                (Int16)((numRef[2] << 8) | numRef[3])
            };
        }
    }
}

I did not use bitwise operators but for unsigned values, this may work:

public (ushort, ushort) SplitToUnsignedShorts(uint value)
{
    ushort v1 = (ushort) (value / 0x10000);
    ushort v2 = (ushort) (value % 0x10000);

    return (v1, v2);
}

Or an expression body version of it:

public (ushort, ushort) SplitToUShorts(uint value)
    => ((ushort)(value / 0x10000), (ushort)(value % 0x10000));

As for signs, you have to decide how you want to split the data. There can only be 1 negative output out of two. Remember a signed value always sacrifices one bit to store the negative state of the number. And that essentially 'halves' the maximum value you can have in that variable. This is also why uint can store twice as much as a signed int.

As for encoding it to your target format, you can either choose make the second number an unsigned short, to preserve the numerical value, or you can manually encode it such that the one bit now represent the sign of that value. This way although you will lose the originally intended numeric value for a sign bit, you don't lose the original binary data and you can always reconstruct it to the original value.

In the end it comes down to how you want to store and process that data. You don't lose the bits, and by extension, the data, as long as you know how to extract the data from (or merge to) your encoded values.

you can use this Nuget package LSharpCode.XExtensions When you had installed it ,you can use it in this way:

using LSharpCode.XExtensions.MathExtensions;
Int32 varInt32name;
Int16 varint16nameLow;
Int16 varint16nameHigh;
varInt32name.ToTwoInt16(out varint16nameLow,out varint16nameHigh);
Related