8-byte double to 32-bit float

I have a problem where I read binary data from serial line, byte by byte. It was no problem to fix integers since they were same size, but the doubles that are sent from the other applications are 8 bytes (64 bits), whereas a float (or double) in Arduino is only 32 bits.

The data I read is little endian and the IEEE754 standard, so it's described at Double-precision floating-point format - Wikipedia. I have not succeded in converting, so I thought I should ask here if anyone has a function that can do this or knows where I can get more information. Do you?

I do not care much about loosing some decimals at the end; I do not work with nuclear safety.

Looking at the two formats on Wikipedia, 32 bit floats have: 1 sign bit, 8 exponent and 23 fraction bits.

Whereas 64 bit floats have: 1 sign bit, 11 exponent and 52 fraction bits.

I might be wrong about this, but I would guess that if you packed the sign bit of the 64bit float into the sign bit of the 32bit float and the most significant 8 bits of the 64bit floats exponent into the 32bit float's exponent bit positions and the same for the most significant bits of the fraction part, then it might just work!

What I am a bit hazy about (my C's not good) is how to actually do this packing. I would guess, you might be able set the bits in an int[2] array (32 bit) and then assign a double variable to it?

Something like (although I may be talking Java):
int rawBits = int[2];
// loops to do your bit copying
double value = (double)rawBits;

Hope this at least points you in the right direction.

Si.

You might do the conversion to 32 bit float before sending if the sending platform is more powerful. Don't know if that is possible?

You might also convert the double to a string before sending. When receiving, the arduino needs to parse and convert but its easy to debug. disadvantage: additional overhead for the parsing.

my 2 cents

if you packed the sign bit of the 64bit float into the sign bit of the 32bit float and the most significant 8 bits of the 64bit floats exponent into the 32bit float's exponent bit positions and the same for the most significant bits of the fraction part, then it might just work!

One must also compensate for the different bias in the exponents.
This will take a pointer to (eight) bytes encoding a double and return a float from it:

float conv(byte *dn)
{
    union {
        float f;
        byte b[4];
    } fn;    
    int expd = ((dn[7] & 127) << 4) + ((dn[6] & 240) >> 4);
    int expf = expd ? (expd - 1024) + 128 : 0;
    fn.b[3] = (dn[7] & 128) + (expf >> 1);
    fn.b[2] = ((expf & 1) << 7) + ((dn[6] & 15) << 3) + ((dn[5] & 0xe0) >> 5);
    fn.b[1] = ((dn[5] & 0x1f) << 3) + ((dn[4] & 0xe0) >> 5);
    fn.b[0] = ((dn[4] & 0x1f) << 3) + ((dn[3] & 0xe0) >> 5);

   return fn.f;
}

(not tested on Inf and NaN)

Thats really clever, so the 'union' allows the same memory to be used for 2 variables, one being the float, the other an array of 4 bytes.

Nice. I feel humble.

One other thing to try (I can't at the moment else I would): declare an Arduino variable of type 'long double'. That might be 64 bits and you can just import it directly.

--
Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

One other thing to try (I can't at the moment else I would): declare an Arduino variable of type 'long double'. That might be 64 bits and you can just import it directly.

I don't see that data type listed in the Arduino reference section.

Lefty

Thank you all!

The "conv" function by drhex works perfectly!
It is just what I needed.

robtillaart suggested to use ascii and that is what I used before, but I thought I would be able to save some time by using binary instead. But as you said, it is easier to understand and debug. For ordinary computers I prefer text to binary formats for (almost) everything, but in Arduino I want higher "framerate".

Si recommended to set the bits of a float. I tried with bitWrite() et ali but the program wouldn't compile. I don't know why!

I think drhex provided the optimal solution!

I'll be saving that union trick for further reference, I had never really seen the point of unions before.

Good to know it worked - just out of interest, what are the readings that you are taking coming from?

Hey there Si,

If you are hungry for literature, you would love these.

This is where I have learnt the bulk of everything, and the author was on the standardisation committee which came up with the C and C++ standards:

These cover the entire c and c++ languages with lots of sample code.

Thanks for the references.

Most of the time in Arduino, the most complex C I need is some bit manipulation. Thats part oft he charm for me, its an antidote to the big software projects day job!

But, I am conscious that there is a lot more to C than the subset I use. Maybe I should just dust off my old copy of Kernighan and Richie and read it cover to cover, its probably been 20 years!

I'll be saving that union trick for further reference, I had never really seen the point of unions before

Beware of unions where you have architectures with different endian-ness.
Code compiled for one end of your serial link may not work at the other.

The data I am reading comes from the flight simulator Flightgear (FG). I then use my Arduino to control thrust, rudders, flaps etc. But now I also want to read some values (rpm, altitude etc) and hopefully display them, if possible with analogic meters.

The version of FG I am using is the older 1.9.1 (current is 2.0) since the newer doesn't work well on my laptop. The endianess in FG is for this version same as the architecture (Intel macbook for me, so little endian). The newer version however is Big Endian, but in 2.0 the floats will be 4 bytes instead of the current 8. So the endianness that may be a future problem with this "conv" function may not be a problem if I just change the protocol to float instead of doubles if I upgrade to the latest FG.

Excellent, that sounds like a lot of fun.

You should be fine driving an analog meter from the PWM outputs. They probably won't event get time to jiggle at PWM frequencies, but if they do, a capacitor will smooth that out.

Yes, we will see where it ends. Since I am not a real flying fan, just a small one, I might give up before I finish it, and continue with some other projects. Future will tell.

I was recommended the TLC5916 in the thread http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1281382049 which is like a shift register with PWM, but do you have any other ideas on how to control more PWMs than the maximum 6 on Deumilanove?

I have never tried it, but have you considered using software PWM.

https://www.webwatcherdata.com/(S(ltzezp45uzorl4qxytpvm0aj))/login.aspx

or you could use an Arduino Mega which has more PWM outputs than you could shake a stick at.

or - and I may get shot down in flames for this, but how about time division multiplexing as if you were driving an LED array. It would work like this (again untried)

1 single analog output to all the positive sides of N volt meters
N regular digital pins to the negative sides of each volt meter - each volt meter with an integrating capacitor to prevent jumpiness.

The algorithm would then be to set the mode of each pin to output in turn, at the same time setting the PWM output to the desired value for that meter (keep the values in an array).

You would have to cycle the meter selecting pins at a much lower frequency than the PWM frequency and do the maths to make sure each meter gets enough PWM cycles during its turn.

The overall effective duty cycle for each meter would be divided by N, but that wouldn't matter as long as you do the maths. You could use milli-ameters rather than volt meters in series with a resistor, then you can pick your own effective voltage range.

It may be a bit jumpy, but messing with the caps or upping the PWM frequency should overcome this.

Thank you for your answer!

At the moment I have 0 volt meters to attach so this issue is not yet an issue. But it may be in future.

can you give any tips on where to find cheap volt meters?

Hi, no, I don't have any meters either. If you want a retro look, you could look for things at a surplus store. I expect a lot of that stuff ends up on eBay now.

Good luck. Si.