views:

1021

answers:

11

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

A: 

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

Laurent Etiemble
He's converting it into *two* Int16s, not one.
Jon Skeet
Read too fast...
Laurent Etiemble
+4  A: 

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.

Toad
The .NET VM is not necessarily little endian. The XBOX 360 runs on the PowerPC architecture and is configured to use big endian. The XNA framework is a version of the .NET Framework by Microsoft for developing games and applications for the XBOX 360. So you can't assume little endiannes.
DrJokepu
I'm not assuming anything. I just chop it into 2 pieces like asked
Toad
reinier: Fair enough. I'm only saying it because if the first and second half have different meanings, you might want to make sure that you get the right half.
DrJokepu
@DrJokepu: I don't believe endianness will affect this at all. The result of shifting is well-defined in terms of the *logical* bits of the integer, regardless of in-memory representation. If you believe there's some observable difference, please provide a sample.
Jon Skeet
I just tested your solution like the one by @Jon and it is not converting fine the value 0x7fffffff, when reconstructed again it fails.
SoMoS
Hi, I tested it too (see my EDIT) and it works fine for the -1 case
Toad
But not for the 0x7FFFFFFF value, just try it.
SoMoS
@somos: good call. I changed the reconstruction code. The problem was that a Negative Int16 gets it's sign bit extended to the full32 bits. Which we don't want so I had to AND it.,
Toad
@reinier: Yes, that was what was wrong with mine too. There's no need to explicitly cast to an int though - that's implicit in the shift.
Jon Skeet
Ok, now the problem is that this construction does not work with Vb.Net as I explain on an edit of the question but this will be a new Question. Thanks.
SoMoS
A: 

If you look at the bit representation, then you are correct.

You can do this with unsigned ints though, as they don't have the sign bit.

Oded
+16  A: 

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.

Jon Skeet
If you don't use a mask in the first case, why use the mask for the second case? Is it that a cast in c# actually tests if there is info in the first part? Or does it chop it off like an and would?
Toad
As I have explained in reinier's answer, you cannot assume little endiannes in .NET.
DrJokepu
@DrJokepu: You *can* assume what shifting and masking will do, however it's stored in memory. A right-shift will be sign-extended, but the cast to `short` will effectively mask it.
Jon Skeet
@Jon Skeet: For simple splitting and reconstitution this is not an issue, however if they have different meanings (e.g. bits 0..15 is an X coordinate and bits 16..31 is a Y coordinate) you will end up mixing up the two. I have no XBOX 360 nor an emulator so I can't test it but it seems to me that this is what would happen.
DrJokepu
I just tested your solution with 0x7FFFFFFF as original, and original = 2147483647 and reconstructed = -1 so something is wrong here.
SoMoS
@DrJokepu, It's about storing, not about meaning after storing. It's a formal issue, not a semantic one. I my view the requirements are met if you can reconstitute it with the same meaning after splitting
Peter
@Somos: Fixed, thanks. I'd forgotten about sign extension on short to int promotion. @DrJokepu: I believe you're wrong, simply because endianness is about the storage, not the *logical* bit representation.
Jon Skeet
isn't this bugged since firstHalf << 16 will always be zero since it will be performed on a short and not on an int? I think that you will have to cast them to int first.
Etan
It implicitly converts it to int, before the shifting.
treaschf
Ok, now the problem is that this construction does not work with Vb.Net as I explain on an edit of the question but this will be a new Question. Thanks.
SoMoS
@Roman Boiko: Given your answer, that's a very strange result IMO. I think I'll leave this answer as it is, as for most other people with a similar problem it may well be a better solution :)
Jon Skeet
In fact, now I think that your behavior is more intuitive than the one which I suggested. :)
Roman Boiko
+3  A: 

Why not? Lets reduce the number of bits for the sake of simplicity : let's say we have 8 bits of which the left bit is a minus bit.

[1001 0110] // representing -22

You can store it in 2 times 4 bits

[1001] [0110] // representing   -1 and 6

I don't see why it wouldn't be possible, you twice have 8 bits info

EDIT : For the sake of simplicity, I didn't just reduce the bits, but also don't use 2-complementmethod. In my examples, the left bit denotes minus, the rest is to be interpreted as a normal positive binary number

Peter
This certainly seems closer to the original intent of the question.
kek444
@Peter: A small nitpick... In 8-bit two's complement `10010110` represents `-106`, and `-22` would actually be represented by `11101010`. Similarly, in 4-bit two's complement `1001` represents `-15`, and `-1` would actually be represented by `1111`.
LukeH
@Luke : it's clearly not two complement :-) , no it's just pseudocode for illustration
Peter
@Peter: Could be confusing for the OP and/or future readers though, if they can't figure out why `10010110` doesn't work out as `-22` in their calculations etc.
LukeH
(Oops, a mistake in my first comment: `1001` represents `-7` and *not* `-15`.)
LukeH
@Luke : they won't run into integers represented by 4 bits and 8 bits any time soon, but since you are right I edited my question with the explanation.
Peter
@Peter: if you use the left bit to denote minus, and the rest as normal number, you have **two** representations of 0: 00000000 and 10000000. And no representations of -32768. Thus **loss of information**. I also had this problem.
Roman Boiko
@Roman : imho you are mingling things, I know what you mean : but still you can transform the 8 bits to 2 x 4 bits without loss of info. In the first example 00000000 and 10000000 indeed both mean zero, but that has nothing to do with the fact that it can be split up.
Peter
A: 

don't worry about sign bit, when u combine both again it will be the same int32

alexm
A: 

You might also be interested in StructLayout or unions if you're using c++.

DanDan
Sorru, using Vb.net here :(
SoMoS
+4  A: 

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);
Agg
I didn't read your solution because I already picked one. But I will use yours. Thanks.
SoMoS
+7  A: 

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.

Roman Boiko
This is what I really was looking for. Thanks a lot. Anyway would be much better to have the unchecked keyword in Vb.Net
SoMoS
If you want to use mine in a generally checked environment, you can always wrap it in "unchecked" :)
Jon Skeet
+2  A: 

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

DanStory
This will work with signed types? I need to create an assembly CLS compliant.
SoMoS
yes, must have skipped that part in your question. I've updated my answer to include signed ints.
DanStory
+2  A: 

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])
            };
        }
    }
}
Roman Boiko
Add /unsafe option to build it.
Roman Boiko
From Reflector: `public static readonly bool IsLittleEndian;`. Thus we **do not** call a property here.
Roman Boiko