0

Here is the code I am currently using, there have been many and various alternatives I have tried all so far without success, this being the latest incarnation sitting within my main program loop (void loop)...

// get temperature from Dallas Temperature Sensor
  sensors.requestTemperatures();
  sen0_data = sensors.getTempC(sensor_0_address);             // read temperature sensor0

// split float
  float f1 = sen0_data;
  byte* fb = (byte*) &f1;

  Serial.println(f1);
  Serial.println(fb[0]);    // Take a look at the bytes
  Serial.println(fb[1]);
  Serial.println(fb[2]);
  Serial.println(fb[3]);

// rebuild float
  temp = (fb[0] << 24) + (fb[1] << 16) + (fb[2] << 8) + (fb[3]);
  Serial.println(temp);

Now for the result as seen in the serial monitor - original float, split byte 0, byte 1, byte 2, byte 3, and finally rebuilt float. What am I doing wrong? as no doubt can be seen from the dirty coding I am a relative newbe The starting float value -127.00 is because I currently have no temperature sensor connected to the Arduino, but I would still expect the rebuilt float to be the same and not -318.00. Even if I connect a sensor the rebuilt float does not mirror the original...

-127.00
0
0
254
194
-318.00

I look forward to any advice... Regards, Simon.

1
  • 2
    AVR is little-endian...
    – Majenko
    Jul 9, 2020 at 22:32

1 Answer 1

1

I have been told to be careful about a simple casting of non-byte variables to an array of bytes because there's no guarantee that the memory allocated for certain data types will be contiguous. I.e., the bytes might be spread out to non-adjacent spots in memory. For example, I was encouraged to use a union to convert a unit64_t, which is 8 bytes, to a byte array on an ESP8266. This involves declaring a union type:

union u64_bytes {
  uint64_t u64;
  uint8_t u8[sizeof(uint64_t)];
};

And then you can use this to convert either way, for example:

  uint64_t myNum = 1;
  u64_bytes splitNum;
  splitNum.u64 = myNum;
  // now i can loop thru the bytes
  for(int i=0; i<sizeof(uint64_t); i++) {
      uint8_t myByte = splitNum.u8[i];
      Serial.print("byte ");
      Serial.print(i);
      Serial.print(" is ");
      Serial.println(myByte);
  }
  // or just refer to individual bytes
  uint8_t oneByte = splitNum.u8[0];
  
  // you can also take bytes and convert the other way
  u64_bytes rebuilt;
  for(int i=0; i<sizeof(splitNum); i++) {
      rebuilt.u8[i] = splitNum.u8[i];
  }
  
  Serial.print("The rebuilt value is ");
  Serial.println(rebuilt.u64);

EDIT: I added some code to show conversion the other way.

That said, it's probably pretty safe in your case to use the bitwise operators you are using. A float is in all likelihood 4 bytes on your hardware, and most hardware allocates memory these days in four-byte blocks.

HOWEVER you still have to take into account the Endianness of the system when it stores values. The basic idea is that if you have four bytes that represent one number, are the most significant bytes first or are the least significant bytes first?

For example, if you are talking about a four byte unsigned integer the number 1, aka 0x00000001, it would be stored like this in Big-Endian hexadecimal:

0x00 0x00 0x00 0x01

But on little endian hardware (like mine) the order is different. They put the least significant bytes first in memory like so:

0x01 0x00 0x00 0x00

This might be very confusing, but the bottom line is you might have to think twice about what the byte order is for your data. I'd add that floats and signed integers might have some aspects that surprise you, and when you try and communicate any information over a serial line (SPI, I2C, etc.) or over a network then you might have to worry about what sort of conversions are happening. You must have to make sure that you know what order your bytes are and you unpack them in a way that's appropriate to the way you packed them.

Lastly, I would encourage you maybe think about (and possibly elaborate) what you are hoping to accomplish by taking apart a float into bytes? If you don't know about the structure of bits inside a float, you would need to get better acquainted with that before you start monkeying with the bytes inside it.

EDIT 2: here's an example where I use a struct to convert a float:

  union float_bytes {
    float flt;
    uint8_t u8[sizeof(float)];
  };

  float myNum = -127.0;
  float_bytes splitNum;
  splitNum.flt = myNum;
  // now i can loop thru the bytes
  for(int i=0; i<sizeof(splitNum); i++) {
      uint8_t myByte = splitNum.u8[i];
      Serial.print("byte ");
      Serial.print(i);
      Serial.print(" is ");
      Serial.println(myByte);
  }
  // or just refer to individual bytes
  uint8_t oneByte = splitNum.u8[0];
  
  // you can also take bytes and convert the other way
  float_bytes rebuilt;
  for(int i=0; i<sizeof(splitNum); i++) {
      rebuilt.u8[i] = splitNum.u8[i];
  }
  
  Serial.print("The rebuilt value is ");
  Serial.println(rebuilt.flt);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.