Difference between rem2pi and mod2pi?

I’m trying to understand the difference between rem2pi(x, RoundNearest) and mod2pi (or more precisely mod2pi - π) from Base, Math functions. Is that correct that roughly speaking both function return the same thing, that is the principal value of an angle between -π and +π?

I understand from the doc that mod(x, y) is the same as rem(x, y, RoundDown), but things gets complicated when it says that mod2pi(x) is different from mod(x, 2π)

When it says mod2pi(x) is different from mod(x, 2π), it just means that mod(x, 2π) uses a floating point representation of 2pi, while mod2pi uses the mathematical number. This makes a difference for large inputs, since the rounding error in 2pi will get multiplied by floor(x/2pi).

1 Like

Thanks. So is it correct that mod2pi(x) is the same as rem2pi(x, RoundDown)?

And then, since the doc of rem2pi(x, RoundDown) says it return a number in [0, 2π], how is mod2pi - π different from rem2pi(x, RoundNearest) which return a number in [-π, π]? Are those the same except for the extraneous substraction (which of course brings some error)? Or are there some input values which return different output values?

julia> rem2pi(-1.0, RoundNearest)
-1.0

julia> mod2pi(-1.0)
5.283185307179586

negative numbers strike again.

1 Like

Good catch! Also, I now realize that my reasoning about substracting π to mod2pi(x) is silly.

So it seems that for my usecase, I should stick with rem2pi(x, RoundNearest).

I’m not sure what you mean by “negative numbers strike again.”, but my first implementation, before discovering rem2pi and friends was (x+π) % 2π - π which at first seems to work but indeed breaks when (x+π) is negative. This lead me to the surprising discovery that rem(x, y) output range depends on the sign of x by default (i.e. when not specifying the rounding mode argument which I didn’t know it existed!). Is it this kind of “surprise for new users” that you were thinking of?

What’s wrong with it? -1.0+2pi=5.283185307179586 so it’s in the correct range…

1 Like

@pierre-haessig This is the same as the difference between the rem and mod functions (as specified by ieee). rem returns a result with the same sign as the divisor. mod is periodic. These definitions correspond when the divisor and dividend have the same sign.

1 Like

Thanks for the context. In fact, I think the suprise for me was mostly about coming from Python where -1 % 3 returns 2 (I just check to be sure!) while Julia returns -1.

Digging further in Python, I just found out about the relatively new (Python 3.7+) function math.remainder which claims to be the “IEEE 754-style remainder”. Since the doc says it returns r such that |r| ≤ y/2, this maps to Julia’s rem used with RoundNearest if I’m not mistaken.

About realizing one of my proposition was silly: it’s just that I thought mod2pi(x) - π would give the principal angle in [-π, π]. But in fact, it should be mod2pi(x+π) - π. Otherwise, the output range is correct, but the value is wrong by 180°…

By the way, I can access the doc of rem(x, y, r::RoundingMode=RoundToZero) through the REPL, but oddly enough I don’t see it in the doc Base.rem which is only about rem(x, y). Why is that?