Skip to content

Fix torch.atan2 for y == 0 with negative x - #2769

Open
LeSingh1 wants to merge 1 commit into
apple:mainfrom
LeSingh1:fix/atan2-quadrant-at-y-zero
Open

Fix torch.atan2 for y == 0 with negative x#2769
LeSingh1 wants to merge 1 commit into
apple:mainfrom
LeSingh1:fix/atan2-quadrant-at-y-zero

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Problem

torch.atan2 returns the wrong quadrant in two cases.

The rotation by π for x < 0 is gated on strictly y > 0, so atan2(0, x) for x < 0 returns 0 instead of π. Separately, the "avoid divide-by-zero" shift adds a fixed +2e-8, which flips the sign of any x in (-1e-8, 0) — moving atan(y / x_safe) into the other half plane while the quadrant coefficients still correct for x < 0, giving another ±π error.

y = [0, 0,  1, -1]
x = [-1, -3, -1e-9, -1e-9]

torch : [ 3.14159,  3.14159,  1.57080, -1.57080]
before: [ 0.00000,  0.00000,  4.71239, -4.71239]
after : [ 3.14159,  3.14159,  1.57080, -1.57080]

Fix

Use greater_equal(y, 0) for the x < 0 rotation, and make the safe shift follow sign(x).

The x == 0 branches keep strict > / <, so atan2(0, 0) == 0 is preserved to match PyTorch.

Testing

Added test_atan2_y0_xnegative and test_atan2_x_tiny_negative. Before: 6 failed, 2 skipped. After: 46 passed, 2 skipped, and the full TestAtan2 class passes 84.

A 2000-element random sweep including zeros and tiny negatives shows 0 mismatches against eager PyTorch, max error 2.4e-7.

The existing TestAtan2 covers random inputs, x == 0, and y == 0 & x == 0 — the y == 0, x < 0 quadrant was never exercised.

`torch.atan2(0, x)` is `pi` for `x < 0`, but the quadrant correction only rotated
by `pi` when `y > 0` strictly, so those elements came back as `0`.

The shift that moves `x` away from zero to avoid dividing by it also flipped the
sign of any `x` in `(-1e-8, 0)`, which put `atan(y / x_safe)` in the opposite half
plane while the quadrant term still corrected for `x < 0`, giving a result off by
`pi`. Make the shift follow the sign of `x`.

    >>> y = torch.tensor([0.0, 0.0, 1.0, -1.0])
    >>> x = torch.tensor([-1.0, -3.0, -1e-9, -1e-9])
    torch : [ 3.14159,  3.14159,  1.57080, -1.57080]
    before: [ 0.00000,  0.00000,  4.71239, -4.71239]
    after : [ 3.14159,  3.14159,  1.57080, -1.57080]
@TobyRoseman

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants