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.
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
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 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
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() {}
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() {}
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!
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 ...