3

I have a need to convert an Int32 value to a 3-byte (24-bit) integer. Endianness remains the same (little), but I cannot figure out how to move the sign appropriately. The values are already constrained to the proper range, I just can't figure out how to convert 4 bytes to 3. Using C# 4.0. This is for hardware integration, so I have to have 24-bit values, cannot use 32 bit.

drharris
  • 11,194
  • 5
  • 43
  • 56
  • What type do you plan on storing the 24-bit integer in? – The Scrum Meister Feb 07 '11 at 06:17
  • A byte array would be just fine. I will be outputting the 3 bytes across a serial connection, so as long as I can get it into bytes, I'm good to go. – drharris Feb 07 '11 at 06:18
  • That third line won't do anything (`i & 0xffffff` is always positive). Also, you might want to follow the second part of Joe's answer that works on systems with different endiannesses; the code you have will work for now but will break in unusual ways if you ever try to run it on a PowerPC or MIPS, for example. – Jeremiah Willcock Feb 07 '11 at 07:08
  • Sorry, my actual code involves setting a `bool` flag when negative. I neglected to realize that line 2 would then make it positive, and so line 3 would never activate. The point was to test the original number for negative. I will update the answer accordingly. And I am accounting for endianness, but I'll make a note in the solution that you should do so. – drharris Feb 07 '11 at 07:11
  • I simply removed my addendum altogether. Any interested party should read the answers. Assuming the 32-bit value is already constrained to the range of a 24-bit integer, the negative sign will always carry over to the 24-bit due to two's complement. All that is left is checking for endianness when you retrieve the byte array. I tried to overcomplicate things. :) – drharris Feb 07 '11 at 07:19

2 Answers2

3

If you want to do that conversion, just remove the top byte of the four-byte number. Two's complement representation will take care of the sign correctly. If you want to keep the 24-bit number in an Int32 variable, you can use v & 0xFFFFFF to get just the lower 24 bits. I saw your comment about the byte array: if you have space in the array, write all four bytes of the number and just send the first three; that is specific to little-endian systems, though.

Jeremiah Willcock
  • 30,161
  • 7
  • 76
  • 78
  • Thanks for the info. I'm still not convinced about the sign, since it's typically held within the most significant bit. For example, try the code `Console.Write(-42 & 0xffffff);`. Or am I missing something here? – drharris Feb 07 '11 at 06:44
  • It will just keep the lowest three bytes, which are unsigned (since you are right about the sign bit being in the top byte). However, even with the mask, `42` and `-42` will produce different results after the AND operation. – Jeremiah Willcock Feb 07 '11 at 06:46
  • So, I can do `int bit24 = bit32 & 0xffffff;`, and then do `bit24 |= 0x800000;` if it tests for negative? Basically resetting that sign bit manually? – drharris Feb 07 '11 at 06:48
  • You would want to do `if (bit24 & 0x800000) bit32 = (bit24 | 0xFF000000); else bit32 = bit24;` to restore the sign bit (since all of the top bits need to be set to keep the value when changing to 32 bits). For a 24-bit number, the sign bit is in a different place than for a 32-bit number. – Jeremiah Willcock Feb 07 '11 at 06:50
  • 2
    My bad. I neglected to actually look at the hex for these answers. It turns out that for negative numbers in 32-bit, that the proper bit for 24-bit will indeed always be set due to two's complement (*assuming* the values are indeed constrained to the range of 24 bit). I've tested the full range of negative values in 32-bit, and it's always the case, so simply masking by `0xffffff` will always work. – drharris Feb 07 '11 at 07:15
3

Found this: http://bytes.com/topic/c-sharp/answers/238589-int-byte

int myInt = 800;
byte[] myByteArray = System.BitConverter.GetBytes(myInt);

sounds like you just need to get the last 3 elements of the array.

EDIT:

as Jeremiah pointed out, you'd need to do something like

int myInt = 800;
byte[] myByteArray = System.BitConverter.GetBytes(myInt);

if (BitConverter.IsLittleEndian) {
    // get the first 3 elements
} else {
    // get the last 3 elements
}
Joe
  • 11,147
  • 7
  • 49
  • 60
  • Note that the documentation says that the result of `GetBytes` is in the system's native endianness, so there would need to be a test for that to be portable. I think you meant "first 3 elements" rather than "last 3" for little-endian, too. – Jeremiah Willcock Feb 07 '11 at 06:25
  • good point, but doesn't all 3 answers have the same flaw? or does v & 0xFFFFFF work because the logic is handled in a lower level? – Joe Feb 07 '11 at 06:30
  • 1
    What flaw? Requiring a little-endian system? All of the solutions that write to a byte array seem to require endianness-checking; `v & 0xFFFFFF` is done within a 32-bit word, without needing to know how that is represented as bytes. – Jeremiah Willcock Feb 07 '11 at 06:32
  • Looking at the docs some more, `BinaryReader` and `BinaryWriter` are always little-endian, regardless of the system. There is also a solution at http://snipplr.com/view/15179/adapt-systembitconverter-to-handle-big-endian-network-byte-ordering-in-order-to-create-number-types-from-bytes-and-viceversa/ that allows the endianness to be set. – Jeremiah Willcock Feb 07 '11 at 06:36
  • Looking at the newest change to your answer, I think it's still backwards: you want the first 3 elements for little-endian and the last 3 for big-endian. – Jeremiah Willcock Feb 07 '11 at 06:37
  • ah ok, i understand it now. i've updated my answer to hopefully be endian-correct now :) – Joe Feb 07 '11 at 06:38
  • I didn't see a change since my last comment. – Jeremiah Willcock Feb 07 '11 at 06:39
  • that's cause i took about 5 minutes to write my comment in which you posted 2 more! :) ur too quick. ok how's that? – Joe Feb 07 '11 at 06:41
  • The second part is right, but the part before "EDIT:" still says "last 3". I would recommend getting rid of the old part anyway. – Jeremiah Willcock Feb 07 '11 at 06:42
  • Tough to decide which one to pick, since the generic use case (bit masking) is properly handled by Jeremiah's answer, but in the end I did need a byte array. Despite knowing this particular method already, it did provide some good information to future people with a similar problem. Final answer explained in the original post's edit. – drharris Feb 07 '11 at 07:06