I'm looking for some nice C code that will accomplish effectively:
while (deltaPhase >= M_PI) deltaPhase -= M_TWOPI;
while (deltaPhase < -M_PI) deltaPhase += M_TWOPI;
What are my options?
I'm looking for some nice C code that will accomplish effectively:
while (deltaPhase >= M_PI) deltaPhase -= M_TWOPI;
while (deltaPhase < -M_PI) deltaPhase += M_TWOPI;
What are my options?
One-liner constant-time solution:
Okay, it's a two-liner if you count the second function for
[min,max)
form, but close enough — you could merge them together anyways.Then you can simply use
deltaPhase = wrapMinMax(deltaPhase, -M_PI, +M_PI)
.The solutions is constant-time, meaning that the time it takes does not depend on how far your value is from
[-PI,+PI)
— for better or for worse.Verification:
Now, I don't expect you to take my word for it, so here are some examples, including boundary conditions. I'm using integers for clarity, but it works much the same with
fmod()
and floats:x
:wrapMax(3, 5) == 3
:(5 + 3 % 5) % 5 == (5 + 3) % 5 == 8 % 5 == 3
wrapMax(6, 5) == 1
:(5 + 6 % 5) % 5 == (5 + 1) % 5 == 6 % 5 == 1
x
:wrapMax(-3, 5) == 2
:(5 + (-3) % 5) % 5 == (5 - 3) % 5 == 2 % 5 == 2
wrapMax(-6, 5) == 4
:(5 + (-6) % 5) % 5 == (5 - 1) % 5 == 4 % 5 == 4
wrapMax(0, 5) == 0
:(5 + 0 % 5) % 5 == (5 + 0) % 5 == 5 % 5 == 0
wrapMax(5, 5) == 0
:(5 + 5 % 5) % 5 == (5 + 0) % 5== 5 % 5 == 0
wrapMax(-5, 5) == 0
:(5 + (-5) % 5) % 5 == (5 + 0) % 5 == 5 % 5 == 0
-0
instead of+0
for floating-point.The
wrapMinMax
function works much the same: wrappingx
to[min,max)
is the same as wrappingx - min
to[0,max-min)
, and then (re-)addingmin
to the result.I don't know what would happen with a negative max, but feel free to check that yourself!
Edit Apr 19, 2013:
Modulo function updated to handle boundary cases as noted by aka.nice and arr_sea:
In the case where fmod() is implemented through truncated division and has the same sign as the dividend, it can be taken advantage of to solve the general problem thusly:
For the case of (-PI, PI]:
And for the case of [-PI, PI):
[Note that this is pseudocode; my original was written in Tcl, and I didn't want to torture everyone with that. I needed the first case, so had to figure this out.]
If linking against glibc's libm (including newlib's implementation) you can access __ieee754_rem_pio2f() and __ieee754_rem_pio2() private functions:
Edit: Just realised that you need to link to libm.a, I couldn't find the symbols declared in libm.so
There is also
fmod
function inmath.h
but the sign causes trouble so that a subsequent operation is needed to make the result fir in the proper range (like you already do with the while's). For big values ofdeltaPhase
this is probably faster than substracting/adding `M_TWOPI' hundreds of times.EDIT: I didn't try it intensively but I think you can use
fmod
this way by handling positive and negative values differently:The computational time is constant (unlike the while solution which gets slower as the absolute value of deltaPhase increases)
A two-liner, non-iterative, tested solution for normalizing arbitrary angles to [-π, π):
Similarly, for [0, 2π):