◆ cos()

IEEEValue boost::simd::cos ( IEEEValue const &  x)

This function object returns the cosine of the input in radians.

Header <boost/simd/function/cos.hpp>
Notes
The regular call to this functor is cos(x), but cos can also be called with two parameters as cos(x, range_) or with a decorator as std_(cos)(x) or restricted_(cos)(x)

range_ is a tag that allows some control on the computation accuracy and speed.

The control is on the reduction routine of the angle to the \([-\pi/4, \pi/4]\) interval.

They actually are 3 reduction routines that respectively are sufficient for small_, medium_ and big_ angle values respecttively, to have (within cover test) one ulp of difference with the according crlibm (correctly rounded math library) result.

Each tag covers respectively intervals \([-A, A]\) with :

float A double A
small_ \(20\pi\) \(20\pi\)
medium_ \(2^6\pi\) \(2^{18}\pi\)
big_ \(\infty\) \(\infty\)

In fact for each scalar singleton or simd vector of angles there are two possibilities :

  • one is to test if all vector element(s) are in the proper range for the consecutive increasing values of A until we reach a good one or the last : the corresponding template tags are small_, medium_ and big_
  • the second is to force directly a reduction method: the corresponding template tags are direct_small_, direct_medium_ and direct_big_
direct_small_ is NOT equivalent to small_ because there are also two other methods for \([0, \pi/4]\) (no reduction) and \([\pi/4, \pi/2]\) (straight reduction) that are not considered in direct small_

Note that for float the direct_big_ case is both early an hyper costly and shall be avoided whenever possible. To partially achieve this aim when double are available on the platform, this part of reduction is delegated to the double precision routines.

Advices
  • If there is no restrictions ever on your angles and you care for precision use the default cos(x) or equivalently cos(x, big_).
  • if you do not care for precision you can use cos(x, medium_) or cos(x, small_) that will be accurate for their proper range and degrade in accuracy with greater values.
Now, the choice of direct or not relies on probabilities computations: assuming that a vector contains k elements and that testing all values that are in an interval takes c cycles and the probability of a value to be in interval \([a, b]\) is \(p(a, b)\) the number of cycles used by a direct \({}_i\) method is simply the reduction time: \(N(\)direct \({}_i)\) On the other side the number of cycles for the non-direct methods will have a more complicated expression :

\(\hspace{5em}\sum_{i=1}^{m} p(A_{i-1}, A_i)^k N(\)direct \({}_i)\)

So the non direct methods will be interesting only if you want accurate results everywhere and have anyhow a big proportion of small angles. This is even more true (if possible) in simd and the more k is big, because of the kth power.
For instance in the medium_ float case:
  • if angles are equidistributed on \([0, 2^{16} \pi]\), the \(p(0, 20\pi)\) will be less than \(2^{-11}\) and thus (for example) if \(k=4\), there will be 1 quadruple over 1.76e+13 falling in the small_ case...
  • Even sorting will do no good because the sort cost will be against the ratio of 1 successful quadruplet over 2048.
  • Contrarily if your angles have a Gaussian distribution with 0 mean and \(10\pi\) standard deviation, 80% of the intervals will be in the "small_" case (95% of the values).
  • Finally for those that are sure of their angles taking place in a fixed range and want speed, three other template tags can be of choice as they use the chosen reduction, but return Nan for any outsider.
clipped_very_small_, clipped_small_ and clipped_medium_
Decorators
  • std_ provides access to std::cos
  • restricted_ is equivalent to the clipped_very_small_ tag
See also
sincos, cosd, cospi
Example:
#include <boost/simd/trigonometric.hpp>
#include <boost/simd/pack.hpp>
#include <iostream>
namespace bs = boost::simd;
using pack_ft = bs::pack <float, 4>;
int main()
{
pack_ft pf = {1.0f, 2.0f, -1.0f, 0.5f};
std::cout
<< "---- simd" << '\n'
<< "<- pf = " << pf << '\n'
<< "-> bs::cos(pf) = " << bs::cos(pf) << '\n';
float xf = 2.0f;
std::cout
<< "---- scalar" << '\n'
<< "<- xf = " << xf << '\n'
<< "-> bs::cos(xf) = " << bs::cos(xf) << '\n';
return 0;
}
Possible output:
---- simd
<- pf = (1, 2, -1, 0.5)
-> bs::cos(pf) = (0.540302, -0.416147, 0.540302, 0.877583)
---- scalar
<- xf = 2
-> bs::cos(xf) = -0.416147