Я хотел бы думать, что я достаточно порядочен в рассмотрении кода других людей … но я в растерянности. Это происходит из библиотеки Doom3 Math. Я полагаю, что это существовало в GPL начиная с Quake 1. Обратите внимание, что это действительно ссылка на math.h. Я предполагаю, что есть способ вычислить косинус … но я не могу понять это. Кто-нибудь объяснит?
ID_INLINE float idMath::Cos16( float a ) {
float s, d;
if ( ( a < 0.0f ) || ( a >= TWO_PI ) ) {
a -= floorf( a / TWO_PI ) * TWO_PI;
}
#if 1
if ( a < PI ) {
if ( a > HALF_PI ) {
a = PI - a;
d = -1.0f;
} else {
d = 1.0f;
}
} else {
if ( a > PI + HALF_PI ) {
a = a - TWO_PI;
d = 1.0f;
} else {
a = PI - a;
d = -1.0f;
}
}
#else
a = PI - a;
if ( fabs( a ) >= HALF_PI ) {
a = ( ( a < 0.0f ) ? -PI : PI ) - a;
d = 1.0f;
} else {
d = -1.0f;
}
#endif
s = a * a;
return d * ( ( ( ( ( -2.605e-07f * s + 2.47609e-05f ) * s - 1.3888397e-03f ) * s + 4.16666418e-02f ) * s - 4.999999963e-01f ) * s + 1.0f );
}
Все условные вещи выглядят так, как будто они складывают угол вниз до одного квадранта (или, может быть, октанта, я не могу понять это …;)) и записываем поправочный коэффициент (d
).
Последняя строка выполняет некое полиномиальное приближение 10-го порядка (возможно, Тейлор или же Чебышева?).* Но это работает только на четных полномочиях, так как cos
это четная функция. Это также использует Метод Хорнера избегать прямого вычисления больших мощностей несколько раз.
Затем он повторно применяет правильный знак, используя d
,
* Чтобы убедиться, что это «довольно» точно, попробуйте запустить следующий код в Octave (вы можете сделать это онлайн, например, Вот):
% One quadrant
a = (-pi/2):0.1:(+pi/2);
% Exact result
y_exact = cos(a);
% Our approximation
s = a .* a;
y_approx = (((((-2.605e-07 .* s + 2.47609e-05) .* s - 1.3888397e-03) .* s + 4.16666418e-02) .* s - 4.999999963e-01) .* s + 1);
% Plot
hold on
plot(a, y_exact, 'b')
plot(a, y_approx, 'r')
Других решений пока нет …