Int to float convertion[Solved]

Hi
I am having issues with converting a Int variabel to a float variabel.

In short i have a modbus register with two uint_16_ registers, i have combined them as Uint32_t with both big and little endian succsessfully.

The issue are when im trying to take this:
"Uin32_t ModbusRegister1" gives the value : 2923938625 (Correct)
float(ModbusRegister1) gives the value : 2923938560.00
HEX version : AE47C341

As seen in the image below, i want it in the format of Float-Little Endian (DCBA) (24.41 are the voltage reading on my sensor), but im getting the UINT32 version only with decimal pointers when converting it to float.

I have tried both little and big endian.

what arduino board are you using?
float is typically 4bytes with a precision of 6 to 7 decimal digits
double is typically 8 bytes with a precision of 15 to 16 decimal digits

on some boards, e.g. arduino uno double is 4bytes

Im running a mkr wifi 1010 with a MKR 485 Shield

uses a Arm® Cortex®-M0 32-bit SAMD21 processor which I expect doubles would be 8bytes
you can check by printing the number of bytes used, e.g.

Serial.println(sizeof(double));

try converting your 32bit ints to doubles

with this:

Serial.print(" Convert ");
Serial.print(sizeof(double(ModbusRegister1))); 

im getting : "Convert 8"

with :

Serial.print(" Convert ");
Serial.print(double(ModbusRegister1)); 

im getting: Convert 1645265729.00

if I run the following on an ESP32

void setup() {
  delay(2000);
  Serial.begin(115200);
  Serial.printf("sizeof(double) %d\n", sizeof(double));
  unsigned long i=2923938625;
  Serial.printf("unsigned long %lu\n", i);
  double x=i;
  Serial.printf("double %f\n",x);
}

void loop() {}

the serial monitor displays

sizeof(double) 8
unsigned long 2923938625
double 2923938625.000000

ran this on a MKRFOX 1200

void setup() {
  delay(2000);
  Serial.begin(115200);
  Serial.print("sizeof(double)");
  Serial.println( sizeof(double));
  unsigned long i=2923938625;
  Serial.print("unsigned long ");
  Serial.println(i);
  double x=i;
  Serial.print("double ");
  Serial.println(x);
}

void loop() {}

serial monitor displays

sizeof(double)8
unsigned long 2923938625
double 2923938625.00

You get the same result as i get, but i want it to become somthing similar to "24.41"

If you see the Image below, I want to convert it to the Float Little Endian. Not just a Integer with decimal points.

Hi @horace,

the trouble starts quite likely with a conversion to float. If you stay in the integer world you have more decimal digits for precision. If this page is correct

float are always stored in 4 byte with a limited precision (in the standard "Arduino world").

Even float on ESP32 is realized with 4 byte.

void setup() {
  Serial.begin(115200);
  Serial.print("sizeof(double)");
  Serial.println( sizeof(double));
  Serial.print("sizeof(float)");
  Serial.println( sizeof(float));

  unsigned long lg = 2923938625;
  Serial.print("unsigned long\t");
  Serial.println(lg);

  double db = lg;
  Serial.print("ulong to double\t");
  Serial.println(db);

  float  ft = lg;
  db = ft;
  Serial.print("float to double\t");
  Serial.println(db);
  Serial.print("float          \t");
  Serial.println(ft);
}

void loop() {}

Thanks for your test sketch :wink: I added some cases. Feel free to test it!

P.S.: The output on Wokwi for an ESP32 controller i

sizeof(double)8
sizeof(float)4
unsigned long	2923938625
ulong to double	2923938625.00
float to double	2923938560.00
float          	2923938560.00

And for the theory behind single-precision floating-point format see here
https://en.wikipedia.org/wiki/Single-precision_floating-point_format

|unsigned long |2923938625|
|ulong to double |2923938625.00|
|float to double |2923938560.00|
|float |2923938560.00|
|Hex |AE47C341|

how do i go from 2923938625 to 24.41 as shown in the image below ?

is this what you are attempting to do

union Data {
  unsigned long i;
  float x;
} data;

void setup() {
  delay(2000);
  Serial.begin(115200);
  data.i=0x41c322d1;
  Serial.print("unsigned long ");
  Serial.println(data.i, HEX);
  Serial.print("float ");
  Serial.println(data.x,3);
}

void loop() {}

which gives

unsigned long 41C322D1
float 24.392

The reason is that you cannot simply convert a float to an integer variable by just exchanging the bytes!

See the link I posted above to the wikipedia:

image

This is the way single-precision floating point data are stored:

  • sign bit
  • exponent
  • significand (the "digits")

thats a step closer, what conversion are you using to get from 1103307473(Hex 41C322D1) to 0x41c322d1; ?

If I recall correctly, to do type punning with a union, there should be a memcpy() to explicitly tell the compiler which of the union members is currently active:

union Data {
  unsigned long i;
  float x;
} data;

void setup() {
  delay(2000);
  Serial.begin(115200);
  data.i=0x41c347ae;
  Serial.print("unsigned long ");
  Serial.println(data.i, HEX);
  memcpy(&data.x, &data.i, sizeof(data.x));
  Serial.print("float ");
  Serial.println(data.x,3);
}

void loop() {}
1 Like

There is no conversion being performed. The number is stored in binary, 110307473 is the decimal representation of the number, HEX 41C33D1 is the hexadecimal representation of the same number. The print() funciton lets you specify a number base when printing integers, the default is DEC for decimal, but in this case HEX has been specified.

union Data {
  unsigned long i;
  float x;
} data;

void setup() {
  delay(2000);
  Serial.begin(115200);
  data.i = 0x41C322D1;
  Serial.print("unsigned long ");
  Serial.println(data.i, HEX); //print in hexadecimal
  Serial.println(data.i);      //print in decimal (assumes (data.i, DEC) )
  Serial.println(data.i, BIN); //print in binary
  Serial.println(data.i, OCT); //print in octal
  memcpy(&data.x, &data.i, sizeof(data.x));
  Serial.print("float ");
  Serial.println(data.x, 3);
}

void loop() {}

Thanks everybody, i got it working now.

Really not sure how this Union works if anybody have a good explanation/youtube/page on this that would be very much appreciate

Is this actually doing anything?

I would imagine &data.x and &data.i have the same value/address, so memcpy() is simply overwriting the bytes with their original values.

Does that code continue to work if you remove the memcpy()?

I have been told that using union like this is not guaranteed to work and so not recommended, although in this case I suspect it is working. It's recommended to use memcpy() for this type of "re-representation", but there is no point simply overwriting the same memory with the same values!

Take it a step further and forget the union. Just have a variable of type uint32_t and one of type float.

Simple. Don't use a union for type punning. Use this:

1 Like

try a web search for C++ union
when using union for this type of conversion you have to be sure of your data type sizes

What result would you expect when the bytes of a single-precision float variable are memcopied to an unsigned long integer?

I would not consider this a valid method unless one is interested in some arbitrary results...

Or did I miss something?

P S.: Just found what I was missing: The TO transmits a float in two steps (2 bytes per step). So it's not a conversion the integer data printed are pointless. It is only required to restore the float after transmission again ... :slight_smile: