Je travaille sur mon implémentation rapide (et précise) du péché en C++, et j'ai un problème concernant la mise à l'échelle efficace de l'angle dans la fonction +- pi/2
gamme.
Ma fonction sin pour +-pi/2 en utilisant les séries de Taylor est la suivante (Note : FLOAT est une macro étendue à float ou double juste pour le repère)
/**
* Sin for 'small' angles, accurate on [-pi/2, pi/2], fairly accurate on [-pi, pi]
*/
// To switch between float and double
#define FLOAT float
FLOAT
my_sin_small(FLOAT x)
{
constexpr FLOAT C1 = 1. / (7. * 6. * 5. * 4. * 3. * 2.);
constexpr FLOAT C2 = -1. / (5. * 4. * 3. * 2.);
constexpr FLOAT C3 = 1. / (3. * 2.);
constexpr FLOAT C4 = -1.;
// Correction for sin(pi/2) = 1, due to the ignored taylor terms
constexpr FLOAT corr = -1. / 0.9998431013994987;
const FLOAT x2 = x * x;
return corr * x * (x2 * (x2 * (x2 * C1 + C2) + C3) + C4);
}
Jusqu'ici tout va bien... Le problème survient lorsque j'essaie de mettre à l'échelle un angle arbitraire dans la plage +-pi/2. Ma solution actuelle est la suivante :
FLOAT
my_sin(FLOAT x)
{
constexpr FLOAT pi = 3.141592653589793238462;
constexpr FLOAT rpi = 1 / pi;
// convert to +-pi/2 range
int n = std::nearbyint(x * rpi);
FLOAT xbar = (n * pi - x) * (2 * (n & 1) - 1);
// (2 * (n % 2) - 1) is a sign correction (see below)
return my_sin_small(xbar);
};
J'ai fait un repère et je perds beaucoup pour la mise à l'échelle +-pi/2.
Tricking avec int(angle/pi + 0.5) est un nope puisque c'est limité à la précision de l'int, nécessite également +- branchements, et j'essaie d'éviter les branches ...
Que dois-je essayer pour améliorer les performances de cette mise à l'échelle ? Je suis à court d'idées.
Résultats des tests de référence pour le flotteur. (Dans le benchmark, l'angle pourrait être hors de la plage de validité de my_sin_small, mais pour le bench, je ne m'en soucie pas...) :
Résultats du benchmarking pour le double.
Correction du signe pour xbar dans my_sin() :
Précision de l'algorithme par rapport à la fonction python sin() :