Ticket #2109: sincos.patch

File sincos.patch, 3.8 KB (added by fsincos, 8 years ago)
  • source/maths/Fixed.cpp

    diff --git a/source/maths/Fixed.cpp b/source/maths/Fixed.cpp
    index e9e3c8c..e73de40 100644
    a b CFixed_15_16 CFixed_15_16::Pi()  
    184184    return CFixed_15_16(205887); // = pi << 16
    185185}
    186186
     187// Based on sup-norm optimal approximations of sin+cos, sin-cos on [0,pi/2] (Remez algorithm)
     188// 2^15*(sin(pi/4*x)-cos(pi/4*x)) ~ 36361*(x-1) - 3600*(x-1)^3
     189// 2^15*(sin(pi/4*x)+cos(pi/4*x)) ~ 46340 - 14281*(x-1)^2 + 709 * (x-1)^4
     190// The idea is to use these symmetric terms in order to get both sin and cos
     191// Errors: e ~ 1.1*10^-4 in both cases.
     192// TODO : Check if this breaks parts of the program that rely on special cases.
    187193void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out)
    188194{
    189     // Based on http://www.coranac.com/2009/07/sines/
    190 
    191     // TODO: this could be made a bit more precise by being careful about scaling
    192 
    193195    typedef CFixed_15_16 fixed;
    194196
    195     fixed c2_pi;
    196     c2_pi.SetInternalValue(41721); // = 2/pi << 16
     197    fixed c4_pi;
     198    c4_pi.SetInternalValue(83443); // = 4/pi << 16
    197199
    198     // Map radians onto the range [0, 4)
    199     fixed z = a.Multiply(c2_pi) % fixed::FromInt(4);
     200    // Map radians onto the range [0, 8)
     201    fixed z = a.Multiply(c4_pi) % fixed::FromInt(8);
    200202
    201     // Map z onto the range [-1, +1] for sin, and the same with z+1 to compute cos
    202     fixed sz, cz;
    203     if (z >= fixed::FromInt(3)) // [3, 4)
    204     {
    205         sz = z - fixed::FromInt(4);
    206         cz = z - fixed::FromInt(3);
    207     }
    208     else if (z >= fixed::FromInt(2)) // [2, 3)
    209     {
    210         sz = fixed::FromInt(2) - z;
    211         cz = z - fixed::FromInt(3);
    212     }
    213     else if (z >= fixed::FromInt(1)) // [1, 2)
     203    // Map z onto the range [0, 2], compute (sin+cos)/2 and (sin-cos)/2
     204    fixed c1,c2,c3,c4,c5;
     205    c1.SetInternalValue(36361);
     206    c2.SetInternalValue(3600);
     207    c3.SetInternalValue(46340);
     208    c4.SetInternalValue(14281);
     209    c5.SetInternalValue(709);
     210
     211    if (z >= fixed::FromInt(4)) // [4,8)
    214212    {
    215         sz = fixed::FromInt(2) - z;
    216         cz = fixed::FromInt(1) - z;
     213        if (z >= fixed::FromInt(6)) // [6,8)
     214        {
     215            // sin(3*pi/2+x) = -cos(x), cos(3*pi/2+x) = sin(x)
     216            fixed zn = z - fixed::FromInt(7);
     217            z  = zn.Multiply(zn);
     218            fixed scdif = zn.Multiply(c1 - z.Multiply(c2));
     219            fixed scsum = c3 - z.Multiply(c4 - z.Multiply(c5));
     220            cos_out = scsum + scdif;
     221            sin_out = scdif - scsum;
     222        }
     223        else // [4,6)
     224        {
     225            // sin(pi+x) = -sin(x), cos(pi+x) = -cos(x)
     226            fixed zn = z - fixed::FromInt(5);
     227            z  = zn.Multiply(zn);
     228            fixed scdif= zn.Multiply(c1 - z.Multiply(c2));
     229            fixed scsum = c3 - z.Multiply(c4 - z.Multiply(c5));
     230            sin_out = -scsum - scdif;
     231            cos_out = scdif - scsum;
     232        }
    217233    }
    218     else // [0, 1)
     234    else // [0,4)
    219235    {
    220         sz = z;
    221         cz = fixed::FromInt(1) - z;
     236        if (z >= fixed::FromInt(2)) // [2,4)
     237        {
     238            // sin (pi/2+x) = cos(x), cos(pi/2+x) = -sin(x)
     239            fixed zn = z - fixed::FromInt(3);
     240            z  = zn.Multiply(zn);
     241            fixed scdif = zn.Multiply(c1 - z.Multiply(c2));
     242            fixed scsum = c3 - z.Multiply(c4 - z.Multiply(c5));
     243            cos_out = -scsum - scdif;
     244            sin_out =  scsum - scdif;
     245        }
     246        else // [0,2)
     247        {
     248            fixed zn = z - fixed::FromInt(1);
     249            z  = zn.Multiply(zn);
     250            fixed scdif = zn.Multiply(c1 - z.Multiply(c2));
     251            fixed scsum = c3 - z.Multiply(c4 - z.Multiply(c5));
     252            sin_out = scsum + scdif;
     253            cos_out = scsum - scdif;
     254        }
    222255    }
    223 
    224     // Third-order (max absolute error ~0.02)
    225 
    226 //  sin_out = (sz / 2).Multiply(fixed::FromInt(3) - sz.Multiply(sz));
    227 //  cos_out = (cz / 2).Multiply(fixed::FromInt(3) - cz.Multiply(cz));
    228 
    229     // Fifth-order (max absolute error ~0.0005)
    230 
    231     fixed sz2 = sz.Multiply(sz);
    232     sin_out = sz.Multiply(fixed::Pi() - sz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - sz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
    233 
    234     fixed cz2 = cz.Multiply(cz);
    235     cos_out = cz.Multiply(fixed::Pi() - cz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - cz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
    236 }
     256}
     257 No newline at end of file