Angles: degrees, radians and Atan2
Radians and why C# uses them, finding a direction’s angle correctly with Atan2, normalising angles, and converting between maths angles and compass bearings.
Math.Sin, Math.Cos and Math.Tan use radians. Mixing them up is one of the most common geometry bugs, so it is worth understanding what a radian is.
What is a radian?
| Degrees | 0° | 30° | 45° | 90° | 180° | 270° | 360° |
|---|---|---|---|---|---|---|---|
static double ToRadians(double deg) => deg * Math.PI / 180;static double ToDegrees(double rad) => rad * 180 / Math.PI; Console.WriteLine(Math.Sin(90)); // the bug: 90 radiansConsole.WriteLine(Math.Sin(ToRadians(90))); // what was meantConsole.WriteLine($"{ToDegrees(1):F4}"); // one radian in degrees0.8939966636005579 1 57.2958
The angle of a direction: use Atan2
dy / dx, and the tangent of the angle is that slope, so it is tempting to write Math.Atan(dy / dx). That is wrong in two ways:
dy / dxloses the signs: a wall going up-right (2, 2) and one going down-left (−2, −2) have the same slope, 1, soAtangives 45° for both, though they point in opposite directions.A vertical wall has dx = 0. Withdoublecoordinates the division gives ±∞, whichAtanhappens to survive; withintcoordinates it throws aDivideByZeroException, and a zero-length wall givesNaN.
Math.Atan2(dy, dx) takes the two values separately, so it knows the quadrant and handles dx = 0. It returns an angle between −π and π. Drag the point around the dial to compare the two.
// Angle of a direction, counter-clockwise from +X, in [0, 360).static double DirectionDeg(double dx, double dy){ double deg = Math.Atan2(dy, dx) * 180 / Math.PI; return deg < 0 ? deg + 360 : deg;} Console.WriteLine($"{Math.Atan(-2.0 / -2.0) * 180 / Math.PI:0.##}"); // Atan: wrong quadrantConsole.WriteLine(DirectionDeg(-2, -2));Console.WriteLine(DirectionDeg(0, 5));Console.WriteLine(DirectionDeg(3, -3));45 225 90 315
Normalising angles
static double To360(double deg){ deg %= 360; // C#'s % keeps the sign: -350 % 360 = -350 return deg < 0 ? deg + 360 : deg;} static double ToPlusMinus180(double deg){ deg = To360(deg); return deg > 180 ? deg - 360 : deg;} Console.WriteLine(To360(370));Console.WriteLine(To360(-350));Console.WriteLine(ToPlusMinus180(270)); // a 270 left turn is a 90 right turnConsole.WriteLine(ToPlusMinus180(350 - 10)); // turn from 10 to 35010 10 -90 -20
Maths angles vs compass bearings
bearing = 90 − angle, normalised to [0, 360).
| Direction | Maths angle | Bearing |
|---|---|---|
Show a hint
Atan2, take the difference between consecutive directions, normalise it to (−180, 180], and use the sign: positive is left.
Show the solution
using Pt = (double X, double Y); static double Dir(Pt a, Pt b) => Math.Atan2(b.Y - a.Y, b.X - a.X) * 180 / Math.PI; static double ToPlusMinus180(double deg){ deg %= 360; if (deg < 0) deg += 360; return deg > 180 ? deg - 360 : deg;} Pt[] run = { (0, 0), (6, 0), (6, 4), (2, 7) }; for (int i = 1; i < run.Length - 1; i++){ double turn = ToPlusMinus180(Dir(run[i], run[i + 1]) - Dir(run[i - 1], run[i])); string side = turn >= 0 ? "left" : "right"; Console.WriteLine($"At {run[i]}: {side} {Math.Abs(turn):F1} deg");}At (6, 0): left 90.0 deg At (6, 4): left 53.1 deg
Remember
C# trig functions use radians: 360° = 2π. Convert with deg * Math.PI / 180.Find a direction’s angle with Math.Atan2(dy, dx)(Y first).Atan(dy / dx)gets the quadrant wrong and fails on vertical lines.Normalise angles before comparing them: [0, 360) for directions, (−180, 180] for turns. Maths angles run counter-clockwise from east; bearings run clockwise from north: bearing = 90 − angle.