Hi All,
I am missing something when it comes to using the extern keyword. I have an interest in the usage of double precision floats even though they are a stretch on an 8 bit AVR machine. By default Arduino processes doubles as floats. I have researched this topic and there are good arguments for using longs or even quads and managing the processes as fixed point. Perhaps use of BigNums? Sometimes this works and is a good solution but other times it is is less satisfactory. I was on the lookout for an implementation of double floats. Double floats hit the mark when it comes to magnitude and precision. This avenue of investigation might turn out to be useless but it is bound to teach me something.
The result of the searching was this reference:
http://forum.arduino.cc/index.php?topic=106614.0;wap2
which links to:
http://www.mikrocontroller.net/topic/85256
which, for me at least is unfortunate , as it is in German. However Google translate makes a fair fist of it and there are worse languages to guess at than German.
This leads to a download: called IEEE754_double.zip.
In this zip file there are are a number of files, however the two of interest are avr_f64.c and avr_f64.h. This looked like I might be able to call it as a library so I created a folder called avr_f64 in the library folder of the Arduino structure and placed the above files in this folder. I then created a small test program:
#include <avr_f64.h>
float64_t a,b,c;
void setup()
{
}
void loop()
{
}
And this complied just fine. Clearly the new type float64_t was recognized. So now to do something with it. I added just one line so the code looks this:
#include <avr_f64.h>
float64_t a,b,c;
void setup()
{
c = f_add(a,b); // where f_add add adds two doubles and returns the result
}
void loop()
{
}
I realize that this won't do anything sensible as a and b are undefined. However I wanted see if it would at least compile. Which it didn't. The IDE reported:
d1.cpp.o: In function setup': C:\DOCUME~1\Fred\LOCALS~1\Temp\build616389023816200979.tmp/d1.cpp:10: undefined reference to
f_add(unsigned long long, unsigned long long)'
Some research (see Found a 64bit double implementation library, but I cannot compile it - Programming Questions - Arduino Forum) on this indicted that it was because the function f_add is a C function and not C++. As a result the "mangling" done to allow overloading in C++ was not done and the linker cannot find the un-mangled function name. The fix is to use an extern keyword to tell the the linker that the function is C not C++ and therefore not to expect mangling. If I read this correctly I need to amend the .h file (see lines 134+ in the .h file).
The fragment of the .h file in question starts off like this:
float64_t f_add(float64_t a, float64_t b); // Returns a+b . Special case: -INF + INF = NaN
float64_t f_sub(float64_t a, float64_t b); // Returns a-b . Special case: INF - INF = NaN
float64_t f_mult(float64_t fa, float64_t fb); // Returns a*b . Special case: +/-INF * 0 = NaN
float64_t f_div(float64_t x, float64_t y); // Returns a/b . Special cases: x/0=NaN , INF/INF = NaN , x/INF = 0 if x!=+/-INF
This shows the definitions of the add, subtract, multiply and divide cases. My research shows I have options in defining how the extern might look:
for example:
extern "C"
{
float64_t f_add(float64_t a, float64_t b); // Returns a+b . Special case: -INF + INF = NaN
float64_t f_sub(float64_t a, float64_t b); // Returns a-b . Special case: INF - INF = NaN
float64_t f_mult(float64_t fa, float64_t fb); // Returns a*b . Special case: +/-INF * 0 = NaN
float64_t f_div(float64_t x, float64_t y); // Returns a/b . Special cases: x/0=NaN , INF/INF = NaN , x/INF = 0 if x!=+/-INF
}
should define all these as externals in C.
However it can also be made to work for f_add only:
extern "C" float64_t f_add(float64_t a, float64_t b); // Returns a+b . Special case: -INF + INF = NaN
So the equivalent fragment looks like this:
extern "C" float64_t f_add(float64_t a, float64_t b); // Returns a+b . Special case: -INF + INF = NaN
float64_t f_sub(float64_t a, float64_t b); // Returns a-b . Special case: INF - INF = NaN
float64_t f_mult(float64_t fa, float64_t fb); // Returns a*b . Special case: +/-INF * 0 = NaN
float64_t f_div(float64_t x, float64_t y); // Returns a/b . Special cases: x/0=NaN , INF/INF = NaN , x/INF = 0 if x!=+/-INF
So when I try the latter case I get a new error:
In file included from C:\arduino-1.0\libraries\avr_f64\avr_f64.c:62:
C:\arduino-1.0\libraries\avr_f64/avr_f64.h:134: error: expected identifier or '(' before string constant
So it seems to want an identifier or a ( before the "C" which is what I am am assuming is the string constant. I don't believe this and think I have missed something in using extern.
If I try the first construct:
extern "C"
{
float64_t f_add(float64_t a, float64_t b); // Returns a+b . Special case: -INF + INF = NaN
float64_t f_sub(float64_t a, float64_t b); // Returns a-b . Special case: INF - INF = NaN
float64_t f_mult(float64_t fa, float64_t fb); // Returns a*b . Special case: +/-INF * 0 = NaN
float64_t f_div(float64_t x, float64_t y); // Returns a/b . Special cases: x/0=NaN , INF/INF = NaN , x/INF = 0 if x!=+/-INF
}
the error reported is the same.
What am I missing??
Thanks in advance, Regards, Fred.