Storing a float as a byte

I am using an NTC to read temperature. I use the Steinhart-Hart formula to calculate the temperature from the Resistance of the NTC. The output of that formula is a float (double integer, 4 bytes).
I also want to log the results in memory. For that I have a couple of choices
Store the floating point variable : that will cost me 32 bits of memory for every temperature value logged
Store the original NTC reading and just pull that through the Steinhart-Hart formula again when needed, that will cost me 16 bits for every temperature logged.
As I really don’t need the high decimal precision that a floating point value can give and as I really didn’t expect my temperature range to go outside -15 to +30 degrees Celcius, both 16 bits and 32 bits seemed serious overkill.

So I was wondering if I just couldn’t store it in in 1 Byte. As the Steinhart-Hart formula gives an approximation of the temperature, one can wonder how precise of a reading one needs, but I decided I wanted a solution of 0.25 degrees, though no doubt 0.5 degrees would have been enough already.
I figured I would use the MSB for the sign bit, that left 7 bits
decimal number 31 is equal to 0b00011111, so to cover my range I needed 5 bits for the temperature that left 2 bits
In those two bits I can store numbers 0-3, that I could use for the decimal fraction.
Extracting the essential bits from the binary level of the float seemed to much of a problem so I mainly worked with the decimal values.
In the function floatToByte I do a couple of essential things: I first check if it is a positive or a negative number as that determines the MSB I need to set in my byte. Then Itake the absolute value of the float so I get rid of o potential minus value, which would complicate calculations.
Then I turn the float into an integer and subtract that from the float. That leaves the decimal fraction. In order to keep at least some precision in that I have to put that in a double. Initially it was my idea to turn the values 0-9 into 0-3, but a simple division by 3 didnt have the desired effect as ‘9’ was sometimes 0.89 which by division would give a 2, So I decided on some simple ‘if’ conditions to convert the double fraction into half a nibble.
I then would shift the 5 bit temperature 2 bits to the right bitwise OR it with the fraction (on the two least significant bits and bitwise OR it with the sign bit.
That gave me temperature values that I can store in 1 Byte of EEPROM.

The way back – from memory byte to sensible temperature. Goes the other way around

byte floatToByte(float temp2) 
{
   byte sign = 128;
  if (temp2 >= 0) {
    sign = 0;
  }
  double temp = abs(temp2);
 
  int heel = int(temp);
  double fractie = temp - heel;
  
  fractie = (temp - heel);
  fractie = fractie * 100;
  byte fractie2 = 0;
  if (fractie >= 25) {
    fractie2 = 1;
  }
  if (fractie >= 50) {
    fractie2 = 2;
  }
  if (fractie >= 75) {
    fractie2 = 3;
  }
  byte temperatuur = (((heel << 2) | fractie2) | sign); 
  return temperatuur; 
  } 

void getTempByte(byte Temp) 
{ 
float temperatuurCelcius; 
if (Temp & 0b10000000) // check negative 
{
 Temp = Temp ^ 0b10000000; // knock out MSB byte 
 double fractie = (Temp & 0b00000011); 
 byte body = (Temp >> 2);
    Serial.println(-1 * (body + (fractie * 0.25)));
  }
  else
  {
    byte fractie = (Temp & 0b00000011);
    byte body = (Temp >> 2);
    Serial.println(body + fractie * 0.25);
  }


}
void setup() {
  Serial.begin(9600);
  byte x = floatToByte(+31.10);
  Serial.println(x, BIN);
  x = floatToByte(+31.25);
  Serial.println(x, BIN);
  x = floatToByte(31.50);
  Serial.println(x, BIN);
  x = floatToByte(+31.75);
  Serial.println(x, BIN);
  x = floatToByte(-31.75);
  Serial.println(x, BIN);
  x = floatToByte(+0.15);
  Serial.println(x, BIN);
  Serial.println("temperatuur ");
  getTempByte(0b1111101);

}
void loop() {
  // put your main code here, to run repeatedly:

}
Advertisements