# Two bytes to floating point number

• 10-26-2019, 07:00 PM
Two bytes to floating point number
Hello

I have a nework analyzer that uses modbus communication. Now I have a problem reading the measured value.

It seems the meter is using two 16bit bytes for each value.
Currently I'm trying to read only Power
So if the measured power consumption is less than 1000W, say 136.71W the first byte is 65024 (decimal) and the second is 13671(decimal).
But if the power goes over 1000W.. say 2034.2W the first byte is 65280 and the second is 20342

So the first byte represents the decimal place and the second the actual value..

Can someone help me how to calculate this so I get actual values in float?

Thank you!
• 10-26-2019, 07:44 PM
defragster
If the storage format is a standard float maybe a union will let it be assembled?

Without actually looking at correct syntax - something like this?
Code:

```typedef union {   struct   {     uint16_t one;     uint16_t two;   } b;   float f; } aFloat_Type;```
Once it compiles a quick test will show which half is which - and if it gives a valid float in the end.
• 10-26-2019, 10:20 PM
neurofun
Just for the sake of correctness.
There is no such thing as a 16bit byte. A byte is 8bits. We are talking about words which are 2 bytes long.

If you calculate the 2's complement of word1 and divide by 256, this apparently results in the number of decimal places, word1dp.
Then it is just a matter of dividing word2 by 10 to the power of word1dp to get the float.
• 10-26-2019, 11:18 PM
Hmmm.. I will try unon in the morning but i think it will not work.
I did some digging and I think I asked the qustion wrong.

I don't get two bytes but I get two Ints.
And I don't know how the uion function is going to work since the secod int already gives me the exact number only without the decimal.

I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

I can't load it over 10kW :) So this makes me think the first int only tells me where the decimal point should be but I cant figure it out how to calculate it.

65024 = 1111111000000000
65280 = 1111111100000000

so maybe for 10KW or more could be 65408 (1111111110000000).
• 10-26-2019, 11:29 PM
@neurofun I think you are right!

2's complement of 65024 is 512 and if I divide by 256 i get 2
2's complement od 65280 is 256 and if I divide by 256 i get 1

How can I calculate the 2's complement I used an onlinecalculatur for this :) invert all 1'sand 0's and add 1?
• 10-26-2019, 11:36 PM
PaulStoffregen
Does the manufacturer of this power meter publish any guidance on how to use the data they provide? Maybe share that info here?
• 10-26-2019, 11:40 PM
It's an Iskra MC666 netwok analyzer. I couldn't ind any data regarding the modbus communication. They do provide windows software for configuration anl live data view. Nothing more. I sniffed the bus and got the register locations but that is about it..

I think neurofun is on th right track here.. I need to change my code so I can test
• 10-26-2019, 11:44 PM
defragster
As @neurofun notes - the number ref names were not correct. But seeing this:
Quote:

the first byte is 65024 (decimal) and the second is 13671(decimal).
But if the power goes over 1000W.. say 2034.2W the first byte is 65280 and the second is 20342
Implied your 'byte' is a 16 bit word of two bytes - or it could not hold those values.

And if the source of these 4 bytes - in two 16 bit parts is a common float representation just divided for transmission, then the union might allow them to be re-assembled.

Working backward SerMon shows this to see what p#2 suggested, which does not match post #1, so those don't appear to be 'float' halves.
Code:

```xx.b.one46531 xx.b.two17160 yy.b.one18022 yy.b.two17662```
Updated for two of the values in p#4:
Quote:

if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second
It like this::
Code:

```typedef union {   struct   {     uint16_t one;     uint16_t two;   } b;   float f; } aFloat_Type; void loop3() {   aFloat_Type xx;   xx.f = 1938.2;   aFloat_Type yy;   yy.f = 46.61;   Serial.print( "xx.f" );   Serial.println( xx.f );   Serial.print( "xx.b.one" );   Serial.println( xx.b.one );   Serial.print( "xx.b.two" );   Serial.println( xx.b.two );   Serial.print( "yy.f" );   Serial.println( yy.f );   Serial.print( "yy.b.one" );   Serial.println( yy.b.one );   Serial.print( "yy.b.two" );   Serial.println( yy.b.two ); }```
Shows this - pardon the spacing:
Code:

```xx.f1938.20 xx.b.one18022 xx.b.two17650 yy.f46.61 yy.b.one28836 yy.b.two16954```
• 10-27-2019, 12:21 AM
@defragster I will try your method in the morning so I can see and learn. But I did a quick test with nerofun's post and it works perfectly. I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here. But I will thy the union method that you suggested.

Thank you!
• 10-27-2019, 12:28 AM
defragster
Quote:

@defragster I will try your method in the morning so I can see and learn. But I did a quick test with nerofun's post and it works perfectly. I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here. But I will thy the union method that you suggested.

Thank you!

What I did is C fun - but solution to the wrong problem given guess from original post.
• 10-27-2019, 12:40 AM
neurofun
Quote:

How can I calculate the 2's complement I used an onlinecalculatur for this :) invert all 1'sand 0's and add 1?

Yes
Quote:

I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

Hmm, the first line doesn't make sense. word1 should be 64768.
Quote:

I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here.

Maybe some kind of obfuscation?
• 10-27-2019, 12:43 AM
defragster
For 2's compliment see : https://forum.arduino.cc/index.php?topic=42601.0 - post #7>#8

or @el_supremo / Pete posted here for two bytes into a 16 bit signed in :: https://forum.arduino.cc/index.php?topic=367579.0
• 10-27-2019, 12:52 AM
el_supremo
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete
• 10-27-2019, 01:00 AM
manitou
Quote:

I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

Can you confirm that for 4.507W you were getting 65024? I was hoping for my theory that value would be 64768
• 10-27-2019, 01:57 AM
el_supremo
Yeah, that one looked wrong to me too.

Pete
• 10-27-2019, 02:02 AM
neurofun
Quote:

Originally Posted by defragster
What I did is C fun - but solution to the wrong problem given guess from original post.

Funny enough, your solution would have worked only if there was something like an unsigned float type in C.
Quote:

Originally Posted by el_supremo
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete

correction: bits # 23...00 Binary Unsigned value (24 bit)

Thanks for clearing up the mystery.
• 10-27-2019, 02:23 AM
el_supremo
Ooopps. Yup, copy and paste error.

Pete
• 10-27-2019, 09:19 PM
bicycleguy
There must be an easier way:
Code:

```float thevalue(uint16_t highword, uint16_t lowword){   int8_t expo = highword>>8;   int32_t mant = ((int32_t)((int16_t)(highword<<8))<<8)+lowword;   float value=(float)mant*pow(10,expo);   Serial.println(value,abs(expo));   return value; }```
• 10-27-2019, 09:28 PM
Quote:

Originally Posted by manitou
Can you confirm that for 4.507W you were getting 65024? I was hoping for my theory that value would be 64768

Sorry not right now. I need to re-program the microcontroller but I assembled it inside the analyzer :(
• 10-27-2019, 09:33 PM
Quote:

Originally Posted by el_supremo
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete

Currently I am using this method :

Code:

`ULone = data[1]/(pow(10,(((uint16_t)(~data[0]+0b01))/256)));`
and if
Code:

```data[0] = 65024 data[1] = 4616```
i get:
Code:

`ULone = 46.16`
The results are all correct so it seem it is the right approach
• 10-27-2019, 10:10 PM
Code:

`ULone = ((data[0]<<8)|data[1])/(pow(10,((uint16_t)(~(data[0]>>8)+0b01))));`
Should this be the correct calculation.. I didn't test it

I think that the calculation inmy previous post works because I didn't go over very large numbers and so it doesn't affect anything since the MSB's are always 0
• 10-27-2019, 11:31 PM
bicycleguy
no,
Code:

```here's from post https://forum.pjrc.com/threads/58155...l=1#post219986 highword 65022        lowword 7616        result -123.456 highword 255        lowword 53191        result -12345 highword 64768        lowword 4507        result 4.507 highword 65024        lowword 4616        result 46.16 highword 65280        lowword 19382        result 1938.2 highword 64769        lowword 57920        result 123.456    //example from manual your's highword 65022        lowword 7616        result 76.160 highword 255        lowword 53191        result 0 highword 64768        lowword 4507        result 4.507 highword 65024        lowword 4616        result 46.16 highword 65280        lowword 19382        result 1938.2 highword 64769        lowword 57920        result 579.200```
• 10-28-2019, 12:35 AM

Code:

`ULone = (((data[0]&0xf)<<16)| data[1])/(pow(10,((~(data[0]>>8)&0xf)+0b01)));`
• 10-28-2019, 01:44 AM
neurofun
Quote:

Originally Posted by bicycleguy
There must be an easier way:
Code:

```float thevalue(uint16_t highword, uint16_t lowword){   int8_t expo = highword>>8;   int32_t mant = ((int32_t)((int16_t)(highword<<8))<<8)+lowword;   float value=(float)mant*pow(10,expo);   Serial.println(value,abs(expo));   return value; }```

Except that the mantissa is unsigned, see data type T5 in the manual.
Code:

```float thevalue(uint16_t highword, uint16_t lowword){   int8_t expo = highword>>8;   uint32_t mant = ((uint32_t)((uint16_t)(highword<<8))<<8)+lowword; //unsigned mantissa   float value=(float)mant*pow(10,expo);   Serial.println(value,abs(expo));   return value; }```
• 10-28-2019, 02:29 AM
bicycleguy
Quote:

Originally Posted by neurofun
Except that the mantissa is unsigned, see data type T5 in the manual.

Yah, mine would be a T6:(