Signature
atan2(float $y, float $x): floatPurpose
atan2() answers a geometry question: given a point at horizontal distance x and vertical distance y from the origin, what is the angle of the line from the origin to that point? The answer comes back in radians, measured counterclockwise from the positive x-axis.
It is similar to atan($y / $x), but it looks at the signs of both arguments separately. That lets it tell apart points that would give the same ratio but lie in different quadrants. It also avoids the division, so x = 0 is not a problem.
Signature
Parameters and defaults
$yis the dividend, the vertical component. It comes first.$xis the divisor, the horizontal component. It comes second.
Neither parameter has a default, so both are required. Integers are accepted and converted to floats. Numeric strings are also accepted when strict types are off.
Return value and failure cases
The function returns a float in radians between -M_PI and M_PI inclusive. It does not return false and does not emit warnings for ordinary numbers.
Calling it with the wrong number of arguments, or with a value that cannot be treated as a number (such as an array or the string "abc"), throws a TypeError.
Example 1: one angle per quadrant
The four points below all have a ratio y / x of either 1 or -1, yet atan2() returns four different angles. Each result is rounded to four decimals so the output stays stable.
$points = [[1, 1], [1, -1], [-1, -1], [-1, 1]];
$angles = [];
foreach ($points as [$y, $x]) {
$angles[] = round(atan2($y, $x), 4);
}
echo json_encode($angles);[0.7854,2.3562,-2.3562,-0.7854]
The values are roughly π/4, 3π/4, -3π/4 and -π/4. Positive results are above the x-axis, and negative results are below it.
Example 2: why not atan(y / x)?
The point x = -1, y = 1 is in the upper-left quadrant. Dividing first gives -1, which erases the information that x was negative. atan() can only return values between -π/2 and π/2, so it reports a point in the lower-right direction instead.
$y = 1;
$x = -1;
echo json_encode([
'atan' => round(atan($y / $x), 4),
'atan2' => round(atan2($y, $x), 4),
]);{"atan":-0.7854,"atan2":2.3562}The atan2() result, about 2.3562 radians, is the correct 135° direction.
Example 3: a compass-style heading in degrees
A common task is turning an offset into a whole-degree heading from 0 to 359. Convert the radians with rad2deg(), then shift negative angles into the 0–360 range.
function headingDegrees(int $dx, int $dy): int
{
$degrees = rad2deg(atan2($dy, $dx));
return (int) round(fmod($degrees + 360, 360));
}
echo json_encode([
headingDegrees(3, 4),
headingDegrees(-3, 4),
headingDegrees(0, -2),
]);[53,127,270]
The point straight down gives -90° from atan2(), which the helper turns into 270°.
Edge cases
- Zero x:
atan2(1, 0)is π/2 andatan2(-1, 0)is -π/2. Usingatan($y / $x)here would divide by zero and throwDivisionByZeroError. - Negative x on the axis: a point on the negative x-axis has an angle of π, and the sign of a zero
ycan matter.atan2(0.0, -1.0)gives π, whileatan2(-0.0, -1.0)gives -π because negative zero is distinct for floats. - The origin:
atan2(0, 0)has no meaningful direction. Check for it yourself when your program needs to treat that case specially. - Range limits: the result can be exactly
M_PIor-M_PI, but never beyond them. Remember this if you compare angles or wrap them into another range. - Rounding after normalizing: if you round a normalized angle to whole degrees, a value such as 359.7 becomes 360. Use
% 360after rounding if you need a strict 0–359 range. - Float precision: radians are floating-point numbers, so compare them with a tolerance or round them before display or tests.
Common mistakes
- Swapping the arguments. The order is
atan2($y, $x), not($x, $y). Swapping them reflects the angle across the diagonal, giving π/2 minus the right answer for points in the first quadrant. - Passing a precomputed ratio. Writing
atan2($y / $x, 1)brings back the quadrant problem and the division-by-zero risk. - Forgetting units. The result is in radians. Use
rad2deg()for degrees, anddeg2rad()before feeding angles tosin()orcos(). - Expecting 0–2π. The range is -π to π. Negative angles are normal and need adjusting only if your display requires a full turn.
- Using screen coordinates without thinking. In many screen systems the y-axis grows downward, so the visual rotation is flipped. Negate
$yif you need a conventional mathematical direction. - Comparing floats with
===. Values likeatan2(1, 1) * 4should be compared with a small tolerance, such asabs($a - M_PI) < 1e-12.
Related functions
atan()returns the arc tangent of a single value, limited to -π/2 through π/2.tan()is the tangent function that these two invert.rad2deg()anddeg2rad()convert between radians and degrees.hypot()computes the distance from the origin and pairs naturally withatan2()when converting Cartesian coordinates to polar form.pi()andM_PIgive the constant for comparing and scaling angles.
Recap
Use atan2($y, $x) whenever you need the direction of a point or vector. It takes the vertical component first, uses both signs to choose the correct quadrant, handles x = 0 without error, and returns a float in radians from -π to π. Convert to degrees only at the edge of your program, and normalize ranges explicitly when you need 0–360.
Tested examples
$points = [[1, 1], [1, -1], [-1, -1], [-1, 1]];
$angles = [];
foreach ($points as [$y, $x]) {
$angles[] = round(atan2($y, $x), 4);
}
echo json_encode($angles);[0.7854,2.3562,-2.3562,-0.7854]
$y = 1;
$x = -1;
echo json_encode([
'atan' => round(atan($y / $x), 4),
'atan2' => round(atan2($y, $x), 4),
]);{"atan":-0.7854,"atan2":2.3562}
function headingDegrees(int $dx, int $dy): int
{
$degrees = rad2deg(atan2($dy, $dx));
return (int) round(fmod($degrees + 360, 360));
}
echo json_encode([
headingDegrees(3, 4),
headingDegrees(-3, 4),
headingDegrees(0, -2),
]);[53,127,270]