Hacker News new | ask | show | jobs
by version_five 1366 days ago
Right, radians are the "natural" units of angle, others generally just make a circle into some integral number of units for convenience, but you always have to go back to radians to actually do calculation.

In the next installment, maybe he'll propose that turns can be limiting because diving up a circle requires the use of fractions, and suggest instead of 1 turn per circle, we make a number that's easily divisible into many integer factors. Maybe 216, or I don't know, 360?

3 comments

The point of the original post is that depending on your field (e.g. game engine), maybe all the calculations you need can be done easier in the unit of convenience (e.g. sine of a turn is easier to calculate than sine of radian), so if that is the case you should stick with the unit of convenience thru all the layers and forget about converting to radians in your code.

And using fraction of a turn is also a very good option, much better than radians in many cases, especially if you chose a power of two fraction (e.g. 1/256), in this case all the modular arithmetic needed for angles comes for free as simple integer overflow, and lookup tables became a simple array access.

If you are making something like a game engine using computer hardware from the past 30 years, you should avoid angle measures to the extent possible.

It is much computationally cheaper and more robust (and easier to reason about) to use vector algebra throughout. Then you have no transcendental functions, just basic arithmetic and the occasional square root. You need the dot product and the wedge product (or combined, the geometric product), and derived concepts like vector projection and rejection.

If you need to store a rotation, you can use a unit-magnitude complex number z = x + iy, where x = cos θ, y = sin θ, without ever needing to calculate the quantity θ directly. If you need to compress it down to one parameter for whatever reason, use the stereographic projection s = y / (1 + x) = (1 – x) / y. Reverse that by x = (1 – s²) / (1 + s²), y = 2s / (1 + s²). [If starting from angle measure for whatever reason s = tan ½θ, sometimes called the "half-tangent".]

The angle measure is the logarithm of the rotation, θi = log z. In some contexts logarithms can be very convenient, but it’s not the simplest or most fundamental representation.

With units of "radians" angle measure is the logarithm of base exp(i) [related to the natural logarithm], and with units of "turns" it is the logarithm of base 1 (sort of).

I mean that’s all well and good until you have an object in your game and you’re like “I’d like this object to be leaning at 45 degrees, oops I mean 0.70710678118 + i*0.70710678118
At a high level you should be expressing something like one of "turn this by the angle between vector (1,0) and vector (1, 1)"; "point this in the direction of vector (1, 1)"; or "turn this by the square root of the rotation i" (i = a quarter turn).

If you use angle measures (of whatever units), when you say "rotate by an eighth of a turn" you are instead going to end up with something internally like: multiply some vector by the matrix

[cos ¼π, –sin ¼π ; sin ¼π, cos ¼π]

which is ultimately the same arithmetic, except you had to compute more intermediate transcendental functions to get there.

If you just have to do this a few times, angle measures (in degrees or whatever) are a convenient human interface because most people are very familiar with it from high school. You can have your code ingest the occasional angle measure and turn it into a vector-relevant internal representation immediately.

P.S. If you write 1/√2 in your code compilers are smart enough to turn that into a floating point number at compile time. :-)

In most game engines, constructing the rotation matrix is trivial. Something like Quaternion.Euler(0, 45, 0). The ultimate position / rotation of any given object in a game is usually a compound transform computed via matrix multiplication anyway, e.g. a model view projection matrix. I'm not sure it's the best way, but that's just how most game engines work.
If your game engine is using quaternions as a canonical internal representation for rotations, it is already following my advice from above.

(Game engine developers are smart people and have lots of practical experience with the benefits of avoiding angle measures, as do developers of computer vision, computer graphics, robotics, physical simulations, aerospace flight control, GIS, etc. etc. tools.)

Yes and no.

The Taylor expansion works out like

   sin θ = θ - θ³/₆ + θ⁵/₁₂₀ - θ⁷/₅₀₄₀ + ⋯
if θ is in radians. This is ideal for small θ but if you want to cover, say, 0<θ<2π you are more likely to use something like

https://en.wikipedia.org/wiki/Chebyshev_polynomials

which are optimized across the range. You could rewrite these just as easily to work in degrees as radians.

One of the best ways to calculate sin and cos is CORDIC,

https://en.wikipedia.org/wiki/CORDIC

which is really based on turns, half-turns, quarter-turns and so forth.

CORDIC is not based on radians or turns; it is based on decomposing the angle into a sum of:

phi_n = atan(2^-n)

and then using an abbreviated sum formula where computing cos(theta + phi_n) depends only on sums and bitshifts.

The small-angle approximations sin(x) ≈ x and cos(x) ≈ 1-x^2/2 are the real killer feature of radians, though, because when you can deal with the loss of accuracy you get to avoid using any loops whatsoever. They're also fundamental to understanding simple physical systems like a pendulum.

"One of the best ways to calculate sin and cos is CORDIC" This is extremely false. Cordic is 1 bit per iteration, while polynomials (Chebyshev or minmax) converge exponentially faster.
You are of course right about the speed, when a fast hardware multiplier is available for the computation of the polynomials.

On the other hand with CORDIC it is extremely easy to reach any desired precision, and in cheap hardware it does not require multipliers.

So CORDIC may be considered as "one of the best ways" depending on how "best" is defined.

Even when developing a polynomial approximation for the fast evaluation of a trigonometric function, it may be useful to use an alternative evaluation method by CORDIC, in order to check that the accuracy of the polynomial approximation is indeed that expected, because for CORDIC it is easier to be certain that it computes what it is intended.

> you always have to go back to radians to actually do calculation.

The article actually argues the opposite: that the common implementations of sine and cosine start by converting their radian based arguments to turns or halfturns by dividing by pi.

That's just because the power series would take ages to converge for large arguments, so you take advantage of periodicity. But the implementation in a floating point world is a different thing than the definition in an infinite series world.

For example, e^x can be implemented by handling the integer and fractional parts separately, for similar reasons. But no one really cares about the functions e^floor(y) and e^(y-floor(y)). They are only useful as part of an implementation trick.

That's really not the only thing going on. Yes, it allows you to take advantage of periodicity. But many common function approximations work best (i.e. not requiring any transform of the argument) over the interval [-1, 1].
This is not the common "implementation" of sine and cosine, its the common argument h, in his use case, he tends to want to calculate turns and half turns most often. He might be able to refactor his functions to optimize for this, but its not exactly something I would expect to be a good idea for library code, people do want to calculate other angles.