I had the same reflection. But looking at the code I think we are comparing apples with pears.
I see that sin is defined as
extern double sin(double __x) __ATTR_CONST__;
and double is not a fixed in bytes according to Double-precision floating-point format - Wikipedia
I'm not sure how to fix this but If you would print the result of sin(1.2) with teensy 3 and uno I expect to get different numbers.
Best regards
Jantje
Paul
From my coding experiences I would not expect many C/C++ issues because Arduino is all source and it communicates with very strictly defined protocols. It would be a different story if you had to link in real binaries (I mean .lib .a .dll .o ...) and not the sources (I mean .c .cpp .h ...) or you had to communicate with other running programs.
The troubles I expect would be with the code that uses the registries directly or uses hardware specific things. I have read you made some simulation code so that should solve most of the migration problems.
Great work I would say.
Best regards
Jantje
Jantje, I had just downloaded the source of avr-gcc and found the same thing. I also found sin.S which (once you get past the license and checks for redefinition) is just
#include "fp32def.h"
#include "asmdef.h"
/* float sin (float A);
The sin() function returns the sine of A, where A is given in radians.
*/
ENTRY sin
push rA3
rcall _U(__fp_rempio2)
pop r0
sbrc r0, 7
subi ZL, -2
rjmp _U(__fp_sinus)
ENDFUNC
Nantonos:
Pual, Pete, thanks for the clarification.
Jantje, I had just downloaded the source of avr-gcc and found the same thing. I also found sin.S which (once you get past the license and checks for redefinition) is just
#include "fp32def.h"
#include "asmdef.h"
/* float sin (float A);
The sin() function returns the sine of A, where A is given in radians.
*/
ENTRY sin
push rA3
rcall _U(__fp_rempio2)
pop r0
sbrc r0, 7
subi ZL, -2
rjmp _U(__fp_sinus)
ENDFUNC
This is just one level to deep for me. :~
This definition would be the one I would expect the UNO to use. Are you sure there is no definition of sin with long double as well?
What does the S in sin.S stand for? I googled it but it is to close to sin to find something relevant quickly.
Best regards
Jantje
PS being someone who doesn't give up easily I hope the S to stand for small or single memory model and then a sin.L (or something else) could stand for large memory model containing the long double version. ]
It seems that .S is being used for assembler files. (And no, I don't particularly undestand the AVR assembler. Showing my age, last time I coded in assembler it was for the Zilog Z80).
It also seems that the (8bit) Arduino family consider float and double to be identical, and to mean 32bit IEEE floating point.
Nantonos:
I also found sin.S which (once you get past the license and checks for redefinition) is just...
Well, that's a bit of sin. The code you posted prepares a small stack frame or preserves a working register (the push/pop), calls another function that actually determines the sin, then makes an adjustment for the sign. In other words, the bulk of the sin function is somewhere else.
I can only say that my code saves the results to an array which is printed after the math has been timed. Could the saving to the array cause the time difference?
.. a clock2clock comparision says teensy shall be ~3times faster than Uno (@16MHz) and teensy is 32bit CM3, so a 32bit fp sin() cannot be "only" 1.6x faster than Uno.. saving to an array cannot create such overhead, indeed..
pito:
.. a clock2clock comparision says teensy shall be ~3times faster than Uno (@16MHz) and teensy is 32bit CM3, so a 32bit fp sin() cannot be "only" 1.6x faster than Uno.. saving to an array cannot create such overhead, indeed..
My 2 cents: A possible explanation may be that the compile options for teensy are not similar to those of UNO.
Best regards
Jantje
There are indeed some complex things going on with this test.
For example, this takes 138 us:
for (int i = 0; i < 3; i++)
{
sinanswers[i] = sin(i);
}
time2 = micros();
But this takes takes 229 us.... almost twice as long, just because the input is offset by 400. Clearly sin()'s execution time is not constant.
for (int i = 0; i < 3; i++)
{
sinanswers[i] = sin(i+400);
}
time2 = micros();
I suspected the slowness was due to computing double precision. But I tried changing sin() to sinf(), and amazingly sinf() takes MUCH longer. Clearly newlib or libgcc is not optimized very well, or some settings aren't quite right. I need to dig into that......
And your test with 1000x (actually 500x) sin cos tan (STM32F100 CM3 @48MHz):
timer = millis;
for (i=0;i<500;i++) {
fsi[i]=sinf((float)i);
fco[i]=cosf((float)i);
fta[i]=tanf((float)i);
}
timer = millis - timer;
printf("\rElapsed time float sin cos tan 500x into array: %u millis\n", timer);
Elapsed time float sin cos tan 500x into array: 31 millis
Such big arrays do not fit into my 8kB RAM so double it for 1000x (=62 millis, yours is 278 ms). Double it again for a double precision fp result.
p.
Hi,
I assume that most of us will use fixed point maths, but for those that have a reason to use float and double, is there an alternative implementation that can be included at compile time or some other work around that provide more recent and faster implementations ?
avr-gcc has floating point algorithms that have been carefully optimized for the AVR architecture.
arm-gcc using newlib presumably has generic algorithms...
Jantje:
What does the S in sin.S stand for? I googled it but it is to close to sin to find something relevant quickly.
The GCC compiler passes .S files through the C preprocessor, so that you can use #ifdef and #define within assembly files (because of the #ifdef, you can have one .S file that has code for several different targets, and the defines are set based on the -m options on the command line). If the file is .s (lowercase), it is passed directly to the assembler and does not go through the preprocessor.