# Wrap2pi: what should it do?

**URL:** <https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441>\
**Category:** General Usage\
**Tags:** question\
**Created:** [September 15, 2024, 5:54pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441 "2024-09-15T17:54:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 5:54pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/1 "2024-09-15T17:54:16Z")

</div>

I wrote the following function:

```julia
"""
    wrap2pi(angle)

Limit the angle to the range -π .. π .
"""
function wrap2pi(angle)
    if angle == pi
        return pi
    end
    num2pi = floor(angle / 2π + 0.5)
    angle - 2π * num2pi
end

```

I am now writing tests for it, and I am not sure how it should behave:

- return pi for pi+2n pi ?
- or return -pi for pi+2n pi?

What would you suggest?

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [September 15, 2024, 5:58pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/2 "2024-09-15T17:58:23Z")

</div>

I think such functions should restrict the answer to the half-open interval (-\pi, \pi]

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 6:07pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/3 "2024-09-15T18:07:48Z")

</div>

But then the function above is wrong. How could I fix it?

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [September 15, 2024, 6:11pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/4 "2024-09-15T18:11:35Z")

</div>

Why not using `rem2pi(x, RoundNearest)` instead?

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 6:17pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/5 "2024-09-15T18:17:11Z")

</div>

> [@giordano](#):
>
> `rem2pi(x, RoundNearest)`

Well, it makes my tests fail:

```julia
  Test threw exception
  Expression: wrap2pi(π) == π
  MethodError: no method matching rem2pi(::Irrational{:π}, ::RoundingMode{:Nearest})

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [September 15, 2024, 6:18pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/6 "2024-09-15T18:18:50Z")

</div>

That doesn’t look hard to fix with a `float` around `pi`.

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [September 15, 2024, 6:20pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/7 "2024-09-15T18:20:47Z")

</div>

This is type unstable when you call with `pi`, you get that:

```julia
julia> wrap2pi(pi)
π = 3.1415926535897...

julia> typeof(ans)
Irrational{:π}

julia> wrap2pi(float(pi))
-3.141592653589793

julia> wrap2pi(2pi)
0.0

julia> typeof(ans)
Float64

```

Was it ~~intentional to be type-unstable~~ (well it’s not type-unstable, unless your calling code is, so then in practice), for pi (as that type) to get that back? Since it doesn’t seem rather useful when used with floats, and you pay for that never true test(?).

Why are you making that function in the first place, and if, not wrapping to 0 …2pi? And either way, you might want to have your units in multiples of pi, and limit to 0…2, and use sinpi?

Faster because (no check and) no longer division, and probably still accurate enough:

```julia
function wrap2pi(angle)
  num2pi = floor(angle * (1/2π) + 0.5)
  angle - 2π * num2pi
end

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 6:25pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/8 "2024-09-15T18:25:12Z")

</div>

Also fails my tests:

```julia
KiteUtils.jl: Transformations: Test Failed at /home/ufechner/repos/KiteUtils.jl/test/runtests.jl:144
  Expression: wrap2pi(π) == π
   Evaluated: -3.141592653589793 == π

```

Of course I could change my tests. No convincing solution yet.

---

<div class="post-metadata">

**Author:** ![DanielVandH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielvandh/32/31134_2.png) [@DanielVandH](https://discourse.julialang.org/u/DanielVandH)\
**Post date:** [September 15, 2024, 6:26pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/9 "2024-09-15T18:26:13Z")

</div>

You’re most likely going to end up converting from `pi` to a float anyway, just return the converted version and change your tests.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 6:51pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/10 "2024-09-15T18:51:53Z")

</div>

Even this fails:

```julia
julia> rem2pi(2pi, RoundNearest) ≈ 0
false

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [September 15, 2024, 6:56pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/11 "2024-09-15T18:56:51Z")

</div>

You know `isapprox(x, 0)` is equivalent to `x == 0`, right? Related recent topic: [History of `isapprox` in languages](https://discourse.julialang.org/t/history-of-isapprox-in-languages/119347)

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [September 15, 2024, 6:58pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/12 "2024-09-15T18:58:15Z")

</div>

Matlab has a [similar function](https://www.mathworks.com/help/map/ref/wraptopi.html) and it outputs over closed interval `[-pi, pi]`:

Another alternative:

```julia
function wrap2pi(x)
   y = rem(x, 2π)
   abs(y) > π && (y -= 2π * sign(y))
   return y
end

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 6:59pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/13 "2024-09-15T18:59:04Z")

</div>

So how shall I write the test then?

---

<div class="post-metadata">

**Author:** ![DanielVandH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielvandh/32/31134_2.png) [@DanielVandH](https://discourse.julialang.org/u/DanielVandH)\
**Post date:** [September 15, 2024, 7:01pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/14 "2024-09-15T19:01:06Z")

</div>

Just adjust the tolerance, as discussed in that linked thread already.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 7:09pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/15 "2024-09-15T19:09:29Z")

</div>

Thank you!

This code passes my test without the need to add any tolerances or conversions.

```julia
# """
# wrap2pi(angle)

# Limit the angle to the range -π .. π .
# """
function wrap2pi(angle::Irrational)
    if angle == π
        return π
    else
        return wrap2pi(float(angle))
    end
end
function wrap2pi(angle)
    y = rem(angle, 2π)
    abs(y) > π && (y -= 2π * sign(y))
    return y
end

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [September 15, 2024, 7:11pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/16 "2024-09-15T19:11:00Z")

</div>

```julia
julia> wrap2pi(ℯ)

julia>

```

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 7:14pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/17 "2024-09-15T19:14:28Z")

</div>

Fixed.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [September 15, 2024, 7:16pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/18 "2024-09-15T19:16:32Z")

</div>

I guess I’m missing why not just defining

```julia
wrap2pi(::typeof(pi)) = float(pi)

```

The branch `if angle == -π` is totally useless since it’ll never be hit (unless someone defines a custom `-pi` irrational number).

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [September 15, 2024, 7:22pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/19 "2024-09-15T19:22:08Z")

</div>

I fixed the branch that was never hit.

I am not following your suggestion otherwise because

```julia
float(pi) == pi
false

```

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [September 15, 2024, 7:27pm UTC](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441/20 "2024-09-15T19:27:24Z")

</div>

This seems simplest to me:

```julia
function wrap2pi(x)
    y = rem2pi(x, RoundNearest)
    isequal(y,-pi) && return float(pi)
    return y
end
wrap2pi(::Irrational{:π}) = pi

```

[Next page](https://discourse.julialang.org/t/wrap2pi-what-should-it-do/119441.md?page=2)
