Stream: ideas

Topic: Add atan2 to the stdlib


view this post on Zulip Aurélien Geron (Sep 21 2026 at 04:46):

I've used atan2 much more frequently than atan in my career, and ChatGPT confirms that it's much more frequent in graphics, robotics, physics engines, geographic mapping, and more. I propose that we add it to the standard library. For example, this could be Dec.atan2:

atan2 : { x: Dec, y: Dec } -> Dec
atan2 = |{ x, y }| {
    if x > 0 {
        (y / x).atan()
    } else if x < 0 {
        (y / x).atan() + if y > 0 { Dec.pi } else { -Dec.pi }
    } else if y > 0 {
        Dec.pi / 2
    } else if y < 0 {
        -Dec.pi / 2
    } else {
        0
    }
}

Most languages define atan2(y, x) (i.e., with y as the first argument). I've been bitten by this surprising convention a few times, which is why I chose to use the record { x, y } to make it explicit.

The special case atan2({ x: 0, y: 0}) is mathematically undefined, but almost all implementations return 0 because that's what IEEE 754 recommends (according to ChatGPT).

For F64, the IEEE 754 standard actually defines special cases for the 4 combinations of +0.0 and -0.0:

* atan2(0.0, 0.0) == 0.0
* atan2(-0.0, 0.0) == -0.0
* atan2(0.0, -0.0) == F64.pi
* atan2(-0.0, -0.0) == -F64.pi

view this post on Zulip Matthieu Pizenberg (Sep 21 2026 at 07:54):

Same here, atan2 is so much more useful :)

view this post on Zulip Richard Feldman (Sep 21 2026 at 15:21):

that all makes sense to me! :thumbs_up:

view this post on Zulip Steve Howell (Sep 21 2026 at 23:23):

Is the idea to implement atan2 in zig or pure Roc?

FWIW I have a Claude-written atan2 in my Roc code in a couple places, including https://github.com/showell/roc-apps/blob/master/canvas_apps/lib/Trig.roc.

view this post on Zulip Aurélien Geron (Sep 21 2026 at 23:24):

I've got Astra implementing it in zig right now, the PR should be available within a few minutes.

view this post on Zulip Aurélien Geron (Sep 21 2026 at 23:25):

It ran some performance tests and found that its Dec.atan2 implementation is about 2.1 times faster than a pure Roc implementation on my machine.

view this post on Zulip Aurélien Geron (Sep 21 2026 at 23:27):

For Dec, it used a division-free integer CORDIC (i.e., it computes angles using a sequence of small rotations).

view this post on Zulip Aurélien Geron (Sep 21 2026 at 23:31):

Here's PR #11565 which adds atan2 to Dec, F32, and F64.

view this post on Zulip Richard Feldman (Sep 22 2026 at 00:07):

@Aurélien Geron I agree about is_negative_zero, good call! :+1:

view this post on Zulip Luke Boswell (Sep 22 2026 at 00:25):

Aurélien Geron said:

Here's PR #11565 which adds atan2 to Dec, F32, and F64.

@Aurélien Geron for any changes to the builtins I've been making fuzz-targets here https://github.com/lukewilliamboswell/roc-fuzz/tree/trunk/examples/builtins. Almost every PR we've found some gaps. Would you be interested in giving that a shot?

view this post on Zulip Luke Boswell (Sep 22 2026 at 00:26):

I'm not sure if it makes sense to fuzz atan2 though...

view this post on Zulip Aurélien Geron (Sep 22 2026 at 00:30):

Sure, I can give it a shot. I've never done this so it will be a fun learning experience.

view this post on Zulip Aurélien Geron (Sep 22 2026 at 00:40):

I asked Astra to tweak Dec.atan to use the same optimizations as Dec.atan2, sharing the same CORDIC implementation. It's almost done, and Dec.atan is now 1.5x faster for inputs greater than 1, because the new implementation avoids computing the reciprocal in this case.

Would you like me to include this change to the same PR or should it be a separate PR?

view this post on Zulip Richard Feldman (Sep 22 2026 at 01:25):

yeah I don't think we need to fuzz atan2 when we're just delegating to a well-tested implementation :smile:

view this post on Zulip Notification Bot (Sep 22 2026 at 02:11):

11 messages were moved here from #beginners > Testing for -0.0 or +0.0 by Luke Boswell.

view this post on Zulip Luke Boswell (Sep 22 2026 at 02:12):

@Richard Feldman I've been surprised by the bugs we've caught in the zig implementation of other builtins though... I've found memory leaks, refcounting bugs, and other strange things in edgecases.

view this post on Zulip Richard Feldman (Sep 22 2026 at 02:12):

ok fair, but like...atan? :sweat_smile:

view this post on Zulip Richard Feldman (Sep 22 2026 at 02:13):

I mean it's fine haha

view this post on Zulip Richard Feldman (Sep 22 2026 at 02:13):

doesn't seem like a blocker to me, but fine to include

view this post on Zulip Luke Boswell (Sep 22 2026 at 02:16):

Yeah I don't know, sometimes there is nothing interesting. I just have been doing it for all of the builtins and most of them I've found strange things I would never have expected.


Last updated: Sep 24 2026 at 15:59 UTC