A 2 byte float?

Is there a way to make one, a lower precision float type of variable using only 2 bytes rather than the usual 4? It would be useful for doing float mathematics on an ATTiny where one has less variable memory space to work with, and would mean that when using union to split this sort of variable in to bytes for sending over I2C/SPI/... you'd be able to send shorter messages. For an array of floats, when you don't need 4 bytes worth of precision, this means you can halve the memory requirements and/or the length of messages to be sent/received.

Is there a way to do this?
Thanks

Yes, 16 bit floats are used in many GPUs these days.

see this discussion

More than one. A technique called mixed point can be used.

All it involves is recasting the interpretation of regular integers.

So you use integers, but a unit might mean 0.01 mile per hour, or whatever, so 700 would be 7.00 and so forth.

Obvsly this takes some thinking to decide what to do in a specific case.

About which if you say more, so can we. Your specific need for floating point and so forth.

a7

Fixed Point?

4 Likes

Side note. You cannot send things like a double or a float over I2C. You would need to shift bits and send them 8 at the time using the write function. This does not work with non-integers AFAIK. You can solve this using a union iirc.

Nope. Undefined behaviour.

2 Likes

Can you write() floats in an other way?

Of course you can. All data are binary, and how you interpret the bits is entirely up to you.

5 Likes

why ? you just push bytes, it's up to the receiver to know what the bytes mean

something like this would probably work

#include <Wire.h>

void sendMyData() {
  float dataToSend = 3.141592; // Example float data to send
  byte* bytes = reinterpret_cast<byte*>(&dataToSend);
  for (size_t i = 0; i < sizeof(float); i++) Wire.write(bytes[i]);
}
void setup() {
  Wire.begin(0x08);         // we are an I2C client with address 8
  Wire.onRequest(sendMyData); // callbakc when a request is received
}

void loop() {}

you could also do

void sendMyData() {
  float dataToSend = 3.141592; // Example float data to send
  byte* bytes = reinterpret_cast<byte*>(&dataToSend);
  Wire.write(bytes, sizeof(float));
}

I said that because the last time I tried to bit shift a float, I got a compile error.. I figured that a union would solve that, which it does not. I am not familiar with reinterpret_cast.

This was al 10 years ago and I have never used a float or double since (no need)

2 Likes

would be interesting to see what the error was (may be passing a float * to a function expecting a uint8_t * ?)

a simple C cast would do too

void sendMyData() {
  float dataToSend = 3.141592; // Example float data to send
  byte* bytes = (byte*) &dataToSend;
  Wire.write(bytes, sizeof(float));
}

I just checked,

float x = 3.14 ;

void loop() {
  // put your main code here, to run repeatedly:
  Wire.write( x ) ;

call of overloaded 'write(float&)' is ambiguous

And shifting....

Wire.write( x<<8 ) ;

invalid operands of types 'float' and 'int' to binary 'operator<<'

ah, yes you can't do that indeed

you need to send the byte stream

  float dataToSend = 3.141592; // Example float data to send
  Wire.write((byte*) &dataToSend, sizeof(float));

To transfer a floating point number (say: 17.35) over the I2C Bus, one can use the Wire.print() function; at the receiver side, ASCII coded bytes are to be collected and then the atof() function be applied to retrieve the original float number.

did you try? (you'd lose in precision)

With that approach, you suffer loss of precision, rounding, etc with both the print() and atof() operations. It's almost guaranteed that the 4 bytes that make up the float on the receive end will be different than the original 4 bytes on the transmit end.

2 Likes

In my case, the Receiver exactly shows: 17.35 which I have sent from Sender using Wire.print(17.35) function.

21:46:37.352 -> 17.35
21:46:38.352 -> 17.35
21:46:39.367 -> 17.35

Please, see post #19.