<story><title>Sin and Cos: The Programmer&apos;s Pals</title><url>http://www.helixsoft.nl/articles/circle/sincos.htm</url></story><parent_chain></parent_chain><comment><author>pcwalton</author><text>&amp;gt; For some reason I can&amp;#x27;t quite put my finger on, some experienced game programmers are reluctant to use atan2() and prefer the dot product.&lt;p&gt;It&amp;#x27;s because atan2, along with all the other inverse trigonometric functions, is incredibly slow. On a GPU, sin and cos may be a couple of cycles, but asin&amp;#x2F;acos&amp;#x2F;atan&amp;#x2F;atan2 may be upwards of 30 or 40.&lt;p&gt;Often times, angles are best avoided when possible for this reason. Normalized vectors end up faster and simpler in many cases.&lt;p&gt;Relevant anecdote: I recently wrote some code to compute normals in 2D and thought I would be clever by encoding the normal as an angle to save space. It turned out that the atan2 call at the end was so slow that it outweighed the cost of everything else I was doing several times over. Lesson learned…</text></comment>
<story><title>Sin and Cos: The Programmer&apos;s Pals</title><url>http://www.helixsoft.nl/articles/circle/sincos.htm</url></story><parent_chain></parent_chain><comment><author>0xfaded</author><text>I&amp;#x27;m half way through writing a blog post on how transcendental functions are computed in glibc, and more importantly how to extend the methods to compute things like&lt;p&gt;&lt;pre&gt;&lt;code&gt;  sin(x) &amp;#x2F; x
  (1 - cos(x)) &amp;#x2F; x^2
&lt;/code&gt;&lt;/pre&gt;
directly without incurring the numerical problems arising from the division.&lt;p&gt;One thing to understand is that the stdlib math functions are accurate to machine precision, i.e. the closest representable value to the actual answer.&lt;p&gt;For 32-bit floats, this is achievable using a 5 or 6 order polynomial approximation. However, there was one surprise I was previously unaware of.&lt;p&gt;Obtaining the correct precision becomes difficult when the result is close to zero, since any absolute error is divided by the magnitude of the result. Silly example, if&lt;p&gt;&lt;pre&gt;&lt;code&gt;  sin(0.000001) ~= 0.1
&lt;/code&gt;&lt;/pre&gt;
Then the absolute error is small (~0.1), but the result is still off by four orders of magnitude.&lt;p&gt;Sometimes this is not a problem. When x is small, sin(x) can be computed by simply returning x. This rule is valid until x^3&amp;#x2F;6 &amp;gt; precision ~= 1e-38, or x = 4e-16. So exact precision is obtainable by simply returning x if x &amp;lt; 1e-16.&lt;p&gt;But now consider:&lt;p&gt;&lt;pre&gt;&lt;code&gt;  x = pi&amp;#x2F;2; cos(x) = 0
&lt;/code&gt;&lt;/pre&gt;
The logical way to compute this is as&lt;p&gt;&lt;pre&gt;&lt;code&gt;  -sin(x - pi&amp;#x2F;2)
&lt;/code&gt;&lt;/pre&gt;
But this creates another problem, since x - pi&amp;#x2F;2 ~= 0, a catastrophic cancellation occurs. Now recall that glibc is required to be accurate to machine precision, which goes down to 1e-38. But x is order 1 and only represents 7 digits, down to 1e-6. So glibc is forced to scour lookup tables to recover the missing digits of pi.&lt;p&gt;This makes computing cos(x &amp;gt; pi&amp;#x2F;4) about twice as expensive than cos(x &amp;lt; pi&amp;#x2F;4), but there is a certain irony since it is very unlikely x is known down to 7+ digits, so those CPU cycles are wasted. How many times have you seen x = sqrt(a&lt;i&gt;a + b&lt;/i&gt;b + c*c); ?&lt;p&gt;On my 4 year old laptop:&lt;p&gt;&lt;pre&gt;&lt;code&gt;  time .&amp;#x2F;a.out 0.5   4.12s
  time .&amp;#x2F;a.out 1.5   7.31s
&lt;/code&gt;&lt;/pre&gt;
On my raspberry pi:&lt;p&gt;&lt;pre&gt;&lt;code&gt;  time .&amp;#x2F;a.out 0.5   1m15.829s
  time .&amp;#x2F;a.out 1.5   1m47.522s&lt;/code&gt;&lt;/pre&gt;</text></comment>