Angles, Radians & the Unit Circle
Geometry & Trigonometry
Chapter 2 · Angles, Radians & the Unit Circle
Chapter 1 showed a conceptual angle-wraparound bug — ordinary subtraction not knowing that headings repeat every 360°. This chapter builds the actual vocabulary every later chapter depends on: radians, the unit circle, and periodicity — and shows a second, genuinely deeper wraparound problem, one that connects directly back to Numerical Methods & Floating-Point Computation's own representation-error material.
Degrees vs. Radians
A radian measures an angle by arc length: one radian is the angle subtended when the arc length equals the circle's own radius. A full circle is 2π radians, which is why π radians equals exactly half a circle: 180°.
math.degrees(math.pi) = 180.0 exactly, and math.radians(180) = 3.141592653589793 — Python's own stored approximation of π. Virtually every math library's trigonometric functions (sin, cos, tan) expect radians, not degrees — a genuinely common source of bugs when a value coming from user input or a design tool in degrees is passed straight into a trig function expecting radians.
The Unit Circle Definitions
For a point on a circle of radius 1 centered at the origin, at angle θ measured counterclockwise from the positive x-axis: cos(θ) is that point's x-coordinate, and sin(θ) is its y-coordinate. tan(θ) = sin(θ)/cos(θ) — the slope of the line from the origin to that point.
| Angle | cos(θ) — x-coord | sin(θ) — y-coord | Quadrant behavior |
|---|---|---|---|
| 0° | 1 | 0 | Positive x-axis |
| 90° | 0 | 1 | Positive y-axis |
| 180° | −1 | 0 | Negative x-axis |
| 270° | 0 | −1 | Negative y-axis |
A Real Surprise: "Exact" Trig Identities Aren't Exactly True in Floating Point
The unit circle says cos(90°)=0 and sin(180°)=0 exactly. In floating point, these are only approximately true — for the same reason Numerical Methods & Floating-Point Computation's own Chapter 2 established: π itself has no exact binary representation, so π/2 passed to cos() is never quite the true mathematical π/2.
cos(math.pi/2) = 6.123233995736766×10⁻¹⁷ — not exactly 0. sin(math.pi) = 1.2246467991473532×10⁻¹⁶ — also not exactly 0. Both errors are tiny, right at the scale of machine epsilon, and harmless for most purposes — but a piece of code that checks if cos(angle) == 0: to detect a right angle, following Numerical Methods & Floating-Point Computation's own Chapter 1 warning about exact equality checks, would never trigger, even at what's conceptually exactly 90°.
tan(θ) is mathematically undefined at θ=90°, since division by cos(90°)=0 is undefined. In floating point, tan(math.pi/2) doesn't raise an error or return infinity — it returns 1.633123935319537×10¹⁶, a huge but perfectly ordinary finite number, because cos(π/2) as actually computed is that tiny 6.12×10⁻¹⁷ value above, not true zero. This is exactly the "dividing by something close to zero" pattern Numerical Methods & Floating-Point Computation's own conditioning chapter flagged — code near a tangent asymptote needs an explicit check, since it will silently return a huge, misleading finite number rather than failing loudly.
Periodicity, and a Second, Deeper Wraparound Problem
sin and cos repeat every 2π; tan repeats every π (since flipping both sin and cos in sign leaves their ratio unchanged). This means a game object's rotation angle can, in principle, be represented by infinitely many equivalent values — θ, θ+2π, θ+4π, and so on. In practice, letting an accumulating angle grow without ever wrapping it back into a bounded range is a real, measurable mistake.
≈27,400 radians. Computing sin() directly on that huge, never-wrapped value gives a relative error of ≈3.15×10⁻⁷ against a high-precision reference. Doing the mathematically identical accumulation but wrapping the angle back into [0, 2π) every single frame instead gives a relative error of just ≈4.54×10⁻¹¹ — nearly four orders of magnitude more accurate, for the exact same sequence of increments.
27,400 has far coarser spacing between representable values than an angle value kept near 2π≈6.28 — every increment added to the large unwrapped value loses more precision to rounding than the same increment added to a small, regularly-wrapped one. Regularly wrapping an accumulating angle isn't just tidier code — it's a genuine, measurable numerical-accuracy improvement, for free.
Where This Connects
| This chapter's finding | What it sets up |
|---|---|
cos(π/2) and sin(π) aren't exactly zero | A concrete reason Chapter 5's rotation-matrix code needs tolerance checks, not exact-equality checks, exactly as Numerical Methods & Floating-Point Computation Chapter 1 warned generally |
| An unwrapped accumulating angle loses real precision | Directly foreshadows Chapter 6's gimbal lock and Chapter 7's quaternions, both of which exist partly to keep repeated rotation accumulation numerically well-behaved |
The unit circle's (cos θ, sin θ) point definition | The literal basis for every rotation matrix built in Chapter 5 |
Hands-On Exercises
A design tool exports a rotation of 45 degrees, but a rendering function expects radians and receives the raw value 45 unconverted. Using this chapter's own degree/radian conversion, compute what angle (in degrees) the renderer will actually display, and explain why the bug might not be obvious from a quick glance at the number 45.
Using this chapter's own verified cos(π/2) and tan(π/2) results, explain why a physics engine checking if abs(cos(angle)) < 1e-9: to detect "this object is at a right angle" is a better design than checking if cos(angle) == 0:, and why a physics engine should specifically guard against dividing by cos(angle) near that same angle.
Using this chapter's own verified 2,000,000-frame experiment, explain in your own words why wrapping an accumulating angle every frame is not just a stylistic preference but a genuine numerical-accuracy improvement — your answer should reference why a large angle value has less usable precision than a small one.
📄 View solutionChapter 2 Quick Reference
- Radians: a full circle is
2πradians;πradians=180°— verified exactly viamath.degrees(math.pi)=180.0 - Unit circle:
cos(θ)= x-coordinate,sin(θ)= y-coordinate,tan(θ)=sin(θ)/cos(θ), for a point at angleθon a radius-1 circle - Verified:
cos(π/2)≈6.12×10⁻¹⁷andsin(π)≈1.22×10⁻¹⁶— not exactly zero, becauseπitself has no exact floating-point representation;tan(π/2)returns a huge but finite number (≈1.63×10¹⁶) rather than failing - Periodicity:
sin/cosrepeat every2π;tanrepeats everyπ - Verified: an angle accumulated over 2,000,000 frames without wrapping has
≈3.15×10⁻⁷relative error in itssin(); wrapping every frame reduces that to≈4.54×10⁻¹¹— nearly 4 orders of magnitude better, for free - Next chapter: Triangles — the Law of Sines and the Law of Cosines, the practical toolkit for solving a triangle from partial information