John Szatmari
John Szatmari

Reputation: 395

Convert floating point byte array to decimal

I receive a 4-byte long array over BLE and I am interested in converting the last two bytes to a floating point decimal representation(float, double, etc) in C#. The original value has the following format:

 1 sign bit 
 4 integer bits 
11 fractional bits

My first attempt was using BitConverter, but I am confused by the procedure.

Example: I receive a byte array values where values[2] = 143 and values[3] = 231. Those 2 bytes combined represent a value in the format specified above. I am not sure, but I think that it would be like this:

SIGN  INT FRACTION  
   0 0000 00000000000

Furthermore, since the value comes in two bytes, I tried to use BitConverter.ToString to get the hex representation and then concatenate the bytes. It is at this point where I am not sure how to continue.

Thank you for your help!

Upvotes: 3

Views: 1118

Answers (1)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

Your question lacks information, however the format seems to be clear, e.g. for given two bytes as

  byte[] arr = new byte[] { 123, 45 };

we have

 01111011 00101101
 ^^  ^^    ..    ^
 ||  ||          |  
 ||  |11 fractional bits = 01100101101 (813)
 ||..|  
 |4 integer bits         =        1111 (15)
 | 
 1 sign bit              =           0 (positive)

let's get all the parts:

 bool isNegative =  (arr[0] & 0b10000000) != 0;
 int intPart     =  (arr[0] & 0b01111000) >> 3;
 int fracPart    = ((arr[0] & 0b00000111) << 8) | arr[1];

Now we should combine all these parts into a number; here is ambiguity: there are many possible different ways to compute fractional part. One of them is

 result = [sign] * (intPart + fracPart / 2**Number_Of_Frac_Bits) 

In our case { 123, 45 } we have

 sign     = 0 (positive)
 intPart  = 1111b = 15 
 fracPart = 01100101101b / (2**11) = 813 / 2048 = 0.39697265625

 result   = 15.39697265625

Implementation:

 // double result = (isNegative ? -1 : 1) * (intPart + fracPart / 2048.0);
 Single result = (isNegative ? -1 : 1) * (intPart + fracPart / 2048f);

 Console.Write(result);

Outcome:

 15.39697

Edit: In case you actually don't have sign bit, but use 2's complement for the integer part (which is unusual - double, float use sign bit; 2's complement is for integer types like Int32, Int16 etc.) I expect just an expectation, 2's complement is unusual in the context) something like

  int intPart  =  (arr[0]) >> 3;
  int fracPart = ((arr[0] & 0b00000111) << 8) | arr[1];

  if (intPart >= 16) 
    intPart = -((intPart ^ 0b11111) + 1);

  Single result = (intPart + fracPart / 2048f);

Another possibility that you in fact use integer value (Int16) with fixed floating point; in this case the conversion is easy:

  1. We obtain Int16 as usual
  2. Then put fixed floating point by dividing 2048.0:

Code:

  // -14.01221 for { 143, 231 }
  Single result = unchecked((Int16) ((arr[0] << 8) | arr[1])) / 2048f;

Or

  Single result = BitConverter.ToInt16(BitConverter.IsLittleEndian 
    ? arr.Reverse().ToArray() 
    : arr, 0) / 2048f;

Upvotes: 4

Related Questions