48

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?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
P i
  • 29,020
  • 36
  • 159
  • 267
  • 3
    Don't forget that the while will not only cumulate errors, but could turn into an infinite loop if fed with an insanely high value (like ldexp( M_PI,55)) – aka.nice Jun 24 '12 at 21:18

15 Answers15

26

Edit Apr 19, 2013:

Modulo function updated to handle boundary cases as noted by aka.nice and arr_sea:

static const double     _PI= 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348;
static const double _TWO_PI= 6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696;

// Floating-point modulo
// The result (the remainder) has same sign as the divisor.
// Similar to matlab's mod(); Not similar to fmod() -   Mod(-3,4)= 1   fmod(-3,4)= -3
template<typename T>
T Mod(T x, T y)
{
    static_assert(!std::numeric_limits<T>::is_exact , "Mod: floating-point type expected");

    if (0. == y)
        return x;

    double m= x - y * floor(x/y);

    // handle boundary cases resulted from floating-point cut off:

    if (y > 0)              // modulo range: [0..y)
    {
        if (m>=y)           // Mod(-1e-16             , 360.    ): m= 360.
            return 0;

        if (m<0 )
        {
            if (y+m == y)
                return 0  ; // just in case...
            else
                return y+m; // Mod(106.81415022205296 , _TWO_PI ): m= -1.421e-14 
        }
    }
    else                    // modulo range: (y..0]
    {
        if (m<=y)           // Mod(1e-16              , -360.   ): m= -360.
            return 0;

        if (m>0 )
        {
            if (y+m == y)
                return 0  ; // just in case...
            else
                return y+m; // Mod(-106.81415022205296, -_TWO_PI): m= 1.421e-14 
        }
    }

    return m;
}

// wrap [rad] angle to [-PI..PI)
inline double WrapPosNegPI(double fAng)
{
    return Mod(fAng + _PI, _TWO_PI) - _PI;
}

// wrap [rad] angle to [0..TWO_PI)
inline double WrapTwoPI(double fAng)
{
    return Mod(fAng, _TWO_PI);
}

// wrap [deg] angle to [-180..180)
inline double WrapPosNeg180(double fAng)
{
    return Mod(fAng + 180., 360.) - 180.;
}

// wrap [deg] angle to [0..360)
inline double Wrap360(double fAng)
{
    return Mod(fAng ,360.);
}
Lior Kogan
  • 19,919
  • 6
  • 53
  • 85
  • 1
    Try this, might fail in IEEE 754 double precision (without extended precision promotion, -ffloat-store) assert(WrapPosNegPI(103.67255756846316) >= - _PI); I found the example with following Smalltalk snippet (1 to: 11111 by: 2) detect: [:i | ((i *Float pi) predecessor / Float pi) floor = i] – aka.nice Jun 24 '12 at 21:05
  • 2
    One problem: Mod(x,360.0) is supposed to wrap things to be within the range [0,360). But this implementation of Mod(-1e-16, 360.0) returns 360.0 when the desired return value is 0.0. This is because the math is trying to return 359.9999999999999999 but that cannot be represented by double precision and is hence rounded to 360.0. One fix might be to first insert the line "x += 10.0*y;" at the beginning of the Mod function to prevent loss of precision causing this glitch. Dirty or elegant... you decide :) – arr_sea Mar 21 '13 at 03:37
  • 11
    -1. *Way* overcomplicated, lots of branching, uses reserved identifiers (those starting with `_[A-Z]`), but perhaps more importantly --- the question is tagged as C, answer is C++. – Tim Čas Apr 25 '15 at 21:34
  • Is there a particular way in which this `Mod()` is better than the standard `fmod()`? – Dolda2000 Dec 11 '15 at 01:09
  • @Dolda2000: it is not better nor worse, it is just defined differently. see section 4 [here](http://www.codeproject.com/Articles/190833/Circular-Values-Math-and-Statistics-with-Cplusplus) for possible definitions of the floating-point modulo function. – Lior Kogan Dec 11 '15 at 06:11
21

If ever your input angle can reach arbitrarily high values, and if continuity matters, you can also try

atan2(sin(x),cos(x))

This will preserve continuity of sin(x) and cos(x) better than modulo for high values of x, especially in single precision (float).

Indeed, exact_value_of_pi - double_precision_approximation ~= 1.22e-16

On the other hand, most library/hardware use a high precision approximation of PI for applying the modulo when evaluating trigonometric functions (though x86 family is known to use a rather poor one).

Result might be in [-pi,pi], you'll have to check the exact bounds.

Personaly, I would prevent any angle to reach several revolutions by wrapping systematically and stick to a fmod solution like the one of boost.

aka.nice
  • 9,100
  • 1
  • 28
  • 40
21

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.

/* change to `float/fmodf` or `long double/fmodl` or `int/%` as appropriate */

/* wrap x -> [0,max) */
double wrapMax(double x, double max)
{
    /* integer math: `(max + x % max) % max` */
    return fmod(max + fmod(x, max), max);
}
/* wrap x -> [min,max) */
double wrapMinMax(double x, double min, double max)
{
    return min + wrapMax(x - min, max - min);
}

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:

  • Positive 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
  • Negative x:
    • Note: These assume that integer modulo copies left-hand sign; if not, you get the above ("Positive") case.
    • 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
  • Boundaries:
    • 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
      • Note: Possibly -0 instead of +0 for floating-point.

The wrapMinMax function works much the same: wrapping x to [min,max) is the same as wrapping x - min to [0,max-min), and then (re-)adding min to the result.

I don't know what would happen with a negative max, but feel free to check that yourself!

Tim Čas
  • 10,501
  • 3
  • 26
  • 32
  • Really, you think that `fmod` is constant-time? `%` is not even constant-time for integers. – Pascal Cuoq Apr 25 '15 at 22:28
  • But that's not what I came here to say: I'm going to write a program that applies your function `wrapMinMax` to random values. Before I write it, do you want to bet that `wrapMinMax` returns values below `min` and/or above `max`? – Pascal Cuoq Apr 25 '15 at 22:30
  • @PascalCuoq: Well, okay, but it is a constant number of instructions executed. And I've written that program that tests this: http://codepad.org/aMbhviku --- note how it has no output with a 10M test size. That is, assuming that `max > min`. And losing some arrogance/patronizing would probably help you in the long run. – Tim Čas Apr 26 '15 at 01:27
  • @PascalCuoq: Here's the integer version: http://codepad.org/YQBtMpQZ --- yes, that `#define double int` is an ugly hack, but I'm lazy. I also forgot to change the `%f` to `%d`, but close enough. – Tim Čas Apr 26 '15 at 01:35
  • Sorry for the tone of my previous comments. What bothers me with your answer, which I should have expressed simply instead of being opaquely sarcastic, is that this is a very old question with plenty of answers, that I do not really see how your answer improves on the existing ones, and that there exists a correct answer but it's a book chapter and it's too much work to summarize at this time for this old question. The title of the book chapter is “Argument reduction for trigonometric functions” and it contains the words “Payne” and “Hanek”. – Pascal Cuoq Apr 26 '15 at 13:38
  • aka.nice's answer is “nice” in a way because it takes advantage of the implementation of the Payne-Hanek algorithm found in a quality libm (a low-quality libm may not contain an implementation of that algorithm and have argument reduction of the level of the naive answers here). Pete Kirkham's answer is also nice, despite circumventing the problem rather than answering it exactly, and it relates to Payne-Hanek too (Payne-Hanek produces the best approximation of fmod(x, π). It is worth having a name only because π is not representable. If π was representable, it would exactly be fmod.) – Pascal Cuoq Apr 26 '15 at 13:44
  • Anyway, here are some examples for your consideration: http://pastebin.com/vqxHyH18 – Pascal Cuoq Apr 26 '15 at 13:46
  • You can also make `max - min` overflow (evaluate to `+inf`) for `min = -0x1.0p1023, max = 0x1.0p1023`. After that, `fmod(…, +inf)` returns NaN. These are not very interesting values of `min` and `max` and this is not a limitation for the OP's use case. – Pascal Cuoq Apr 26 '15 at 14:07
  • @PascalCuoq: I strongly disagree. None of the answers are as simple as this with decent enough results and speed. Mine is also extensible for other values of `min` and `max` which, say, the `atan2` solution is not. So yes, it does contribute. And the overflow is a silly point using artificial values. If his values are anywhere near positive or negative `0x1.0p1023`, then this `wrapMinMax()` call is the least of his problems. – Tim Čas Apr 26 '15 at 15:04
  • The four examples I gave in a link may not be a problem for a hypothetical user, but most previous answers get them right, so “decent” must be in the eye of the beholder. – Pascal Cuoq Apr 26 '15 at 15:24
  • For the return value of `wrapMax` function, I changed it from `return fmod(max + fmod(x, max), max);` to `return std::fmod(value, max);` and it outputs exactly the same thing (and so does `wrapMinMax`). So is there any point in doing it your way? – Apple_Banana Jun 16 '21 at 23:46
  • @Apple_Banana Non-negative numbers are not the problem. Try `wrapMax(-5.0, M_PI)` vs `fmod(-5.0, M_PI)`. – Tim Čas Jun 17 '21 at 17:57
  • How about this actual one-liner that is inspired by your original solution? ```return (((x - min) / (max - min)) % 1) * (max - min) + min;``` – Emre Akı Apr 15 '22 at 15:29
9

There is also fmod function in math.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 of deltaPhase this is probably faster than substracting/adding `M_TWOPI' hundreds of times.

deltaPhase = fmod(deltaPhase, M_TWOPI);

EDIT: I didn't try it intensively but I think you can use fmod this way by handling positive and negative values differently:

    if (deltaPhase>0)
        deltaPhase = fmod(deltaPhase+M_PI, 2.0*M_PI)-M_PI;
    else
        deltaPhase = fmod(deltaPhase-M_PI, 2.0*M_PI)+M_PI;

The computational time is constant (unlike the while solution which gets slower as the absolute value of deltaPhase increases)

jdehaan
  • 19,700
  • 6
  • 57
  • 97
8

I would do this:

double wrap(double x) {
    return x-2*M_PI*floor(x/(2*M_PI)+0.5);  
}

There will be significant numerical errors. The best solution to the numerical errors is to store your phase scaled by 1/PI or by 1/(2*PI) and depending on what you are doing store them as fixed point.

6

Here is a version for other people finding this question that can use C++ with Boost:

#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/sign.hpp>

template<typename T>
inline T normalizeRadiansPiToMinusPi(T rad)
{
  // copy the sign of the value in radians to the value of pi
  T signedPI = boost::math::copysign(boost::math::constants::pi<T>(),rad);
  // set the value of rad to the appropriate signed value between pi and -pi
  rad = fmod(rad+signedPI,(2*boost::math::constants::pi<T>())) - signedPI;

  return rad;
} 

C++11 version, no Boost dependency:

#include <cmath>

// Bring the 'difference' between two angles into [-pi; pi].
template <typename T>
T normalizeRadiansPiToMinusPi(T rad) {
  // Copy the sign of the value in radians to the value of pi.
  T signed_pi = std::copysign(M_PI,rad);
  // Set the value of difference to the appropriate signed value between pi and -pi.
  rad = std::fmod(rad + signed_pi,(2 * M_PI)) - signed_pi;
  return rad;
}
Andrew Hundt
  • 2,551
  • 2
  • 32
  • 64
  • how about wrapping an angle between (-pi/2, pi/2)? – CroCo Nov 13 '14 at 23:24
  • 1
    @CroCo just divide the output of this function by 2, right? – Andrew Hundt Nov 14 '14 at 21:33
  • 1
    While the above method is succinct, I just want to point out that output angles are in range [-pi, pi] and not [-p, pi) as the original question was asking for. – iNFINITEi Oct 19 '17 at 04:04
  • Wow I've been using both versions for years and I never noticed that, thanks for the insight! In my use case that isn't a problem, I think an extra if statement for that exact value could fix it but I'm open to a better approach. – Andrew Hundt Oct 19 '17 at 16:54
  • 2
    `boost::math::constants::pi()` my god, boost should truly die. You have to have a special talent of making simple things so hard to memorize, use and understand while reading. I know this is the "C++ way" of doing things, but then it means something has gone wrong along the way for C++. I'm happy I always avoided using boost. – mojuba Nov 01 '19 at 11:50
6

Instead of working in radians, use angles scaled by 1/(2π) and use modf, floor etc. Convert back to radians to use library functions.

This also has the effect that rotating ten thousand and a half revolutions is the same as rotating half then ten thousand revolutions, which is not guaranteed if your angles are in radians, as you have an exact representation in the floating point value rather than summing approximate representations:

#include <iostream>
#include <cmath>

float wrap_rads ( float r )
{
    while ( r > M_PI ) {
        r -= 2 * M_PI;
    }

    while ( r <= -M_PI ) {
        r += 2 * M_PI;
    }

    return r;
}
float wrap_grads ( float r )
{
    float i;
    r = modff ( r, &i );

    if ( r > 0.5 ) r -= 1;
    if ( r <= -0.5 ) r += 1;

    return r;
}

int main ()
{
    for (int rotations = 1; rotations < 100000; rotations *= 10 ) {
    {
        float pi = ( float ) M_PI;
        float two_pi = 2 * pi;

        float a = pi;
        a += rotations * two_pi;

        std::cout << rotations << " and a half rotations in radians " << a << " => " << wrap_rads ( a ) / two_pi << '\n' ;
    }
    {
        float pi = ( float ) 0.5;
        float two_pi = 2 * pi;

        float a = pi;
        a += rotations * two_pi;

        std::cout << rotations << " and a half rotations in grads " << a << " => " << wrap_grads ( a ) / two_pi << '\n' ;
    }
    std::cout << '\n';
}}
Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171
  • Too bad there aren't standard library functions which assume a whole circle represents a value of 1, 2, or 4 [depending upon whether one wants to count rotations, pis, or quadrants], since the range reduction would be much easier. From a practical perspective, even if multiplying by pi before the computation would add a potential rounding error, using a power of two per revolution would almost always improve precision in earlier calculations more than that least rounding error would harm it. – supercat Dec 19 '13 at 21:48
5

I encountered this question when searching for how to wrap a floating point value (or a double) between two arbitrary numbers. It didn't answer specifically for my case, so I worked out my own solution which can be seen here. This will take a given value and wrap it between lowerBound and upperBound where upperBound perfectly meets lowerBound such that they are equivalent (ie: 360 degrees == 0 degrees so 360 would wrap to 0)

Hopefully this answer is helpful to others stumbling across this question looking for a more generic bounding solution.

double boundBetween(double val, double lowerBound, double upperBound){
   if(lowerBound > upperBound){std::swap(lowerBound, upperBound);}
   val-=lowerBound; //adjust to 0
   double rangeSize = upperBound - lowerBound;
   if(rangeSize == 0){return upperBound;} //avoid dividing by 0
   return val - (rangeSize * std::floor(val/rangeSize)) + lowerBound;
}

A related question for integers is available here: Clean, efficient algorithm for wrapping integers in C++

Community
  • 1
  • 1
M2tM
  • 4,415
  • 1
  • 34
  • 43
3

A two-liner, non-iterative, tested solution for normalizing arbitrary angles to [-π, π):

double normalizeAngle(double angle)
{
    double a = fmod(angle + M_PI, 2 * M_PI);
    return a >= 0 ? (a - M_PI) : (a + M_PI);
}

Similarly, for [0, 2π):

double normalizeAngle(double angle)
{
    double a = fmod(angle, 2 * M_PI);
    return a >= 0 ? a : (a + 2 * M_PI);
}
mojuba
  • 11,842
  • 9
  • 51
  • 72
2

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]:

if (x > 0) x = x - 2PI * ceil(x/2PI)  #Shift to the negative regime
return fmod(x - PI, 2PI) + PI

And for the case of [-PI, PI):

if (x < 0) x = x - 2PI * floor(x/2PI)  #Shift to the positive regime
return fmod(x + PI, 2PI) - 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.]

Glenn
  • 21
  • 1
0

I have used (in python):

def WrapAngle(Wrapped, UnWrapped ):
    TWOPI = math.pi * 2
    TWOPIINV = 1.0 / TWOPI
    return  UnWrapped + round((Wrapped - UnWrapped) * TWOPIINV) * TWOPI

c-code equivalent:

#define TWOPI 6.28318531

double WrapAngle(const double dWrapped, const double dUnWrapped )
{   
    const double TWOPIINV = 1.0/ TWOPI;
    return  dUnWrapped + round((dWrapped - dUnWrapped) * TWOPIINV) * TWOPI;
}

notice that this brings it in the wrapped domain +/- 2pi so for +/- pi domain you need to handle that afterward like:

if( angle > pi):
    angle -= 2*math.pi
Henrik
  • 2,180
  • 16
  • 29
0

The way suggested you suggested is best. It is fastest for small deflections. If angles in your program are constantly being deflected into the proper range, then you should only run into big out of range values rarely. Therefore paying the cost of a complicated modular arithmetic code every round seems wasteful. Comparisons are cheap compared to modular arithmetic (http://embeddedgurus.com/stack-overflow/2011/02/efficient-c-tip-13-use-the-modulus-operator-with-caution/).

Tom Larkworthy
  • 2,104
  • 1
  • 20
  • 29
0

In C99:

float unwindRadians( float radians )
{
   const bool radiansNeedUnwinding = radians < -M_PI || M_PI <= radians;

   if ( radiansNeedUnwinding )
   {
      if ( signbit( radians ) )
      {
         radians = -fmodf( -radians + M_PI, 2.f * M_PI ) + M_PI;
      }
      else
      {
         radians = fmodf( radians + M_PI, 2.f * M_PI ) - M_PI;
      }
   }

   return radians;
}
otto
  • 2,230
  • 2
  • 26
  • 26
0

If linking against glibc's libm (including newlib's implementation) you can access __ieee754_rem_pio2f() and __ieee754_rem_pio2() private functions:

extern __int32_t __ieee754_rem_pio2f (float,float*);

float wrapToPI(float xf){
const float p[4]={0,M_PI_2,M_PI,-M_PI_2};

    float yf[2];
    int q;
    int qmod4;

    q=__ieee754_rem_pio2f(xf,yf);

/* xf = q * M_PI_2 + yf[0] + yf[1]                 /
 * yf[1] << y[0], not sure if it could be ignored */

    qmod4= q % 4;

    if (qmod4==2) 
      /* (yf[0] > 0) defines interval (-pi,pi]*/
      return ( (yf[0] > 0) ?  -p[2] : p[2] ) + yf[0] + yf[1];
    else
      return p[qmod4] + yf[0] + yf[1];
}

Edit: Just realised that you need to link to libm.a, I couldn't find the symbols declared in libm.so

xvan
  • 4,554
  • 1
  • 22
  • 37
0

deltaPhase -= floor(deltaPhase/M_TWOPI)*M_TWOPI;

lijie
  • 4,811
  • 22
  • 26
  • 2
    This code produces inexact results and should never be used. `fmod` exists for a reason. – R.. GitHub STOP HELPING ICE Jan 08 '11 at 13:32
  • Out of curiosity, what is wrong with this? I have tested it and it works fine. Can someone give an instance where it would fail? – P i Feb 01 '11 at 05:05
  • seeing as nobody is willing to point out a flaw in this, I am casting my vote to put it up to 0 – P i May 08 '11 at 17:27
  • 3
    @P i: Let c be in `[0, 1)` and let `deltaPhase=-c*PI`. Then we get `-c*PI - (-1)*2*PI`, which equals `(2-c)*PI`, which is not in `[-pi, pi)`. So I would withdraw your positive vote. – Joseph Quinsey Nov 13 '11 at 02:09