2

Context: I'm creating a map-replanner for a robot in C, it should add obstacles within the environment to the map depending on which IR-sensors are getting high readings.

For this i need to check how the robot is positioned IRL vs in the internal map (pointing north). But for some reason, i sometimes get negative values in the conversion. Any ideas what i'm doing wrong?

There should never be any negative values. The allowed values should lie between 0-360. As of now, i get negative values sometimes.

#define     PI   3.14159265358979323846264338327950
#define     DEG(x)   (x*57.2957795130823208767981548141)

Code:

float currentRot  = now.th; //current rotation-reading in RAD from function.
if(currentRot > PI){        //normalize values       
   currentRot     = currentRot - 2 * PI;
}
if(currentRot < -PI){
   currentRot     = currentRot + 2 * PI;
}
currentRot        = fmod(currentRot,PI*2); //convert into RAD-modular
currentRot        = DEG(currentRot);       //convert to degrees

Any ideas of what i'm doing wrong?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Joel
  • 5,732
  • 4
  • 37
  • 65
  • @StoryTeller is right. And Joel, 400 reputation and not a minimal complete example? :/ – gsamaras Oct 08 '17 at 15:03
  • @gsamaras updated. This is literally all that is needed for the conversion. Any further code is redundant – Joel Oct 08 '17 at 15:04
  • 2
    First you try to normalise the range to − _π_ < _x_ < _π_, then you apply `fmod`, whose result has the same sign as _x_. So why do you expect that there can't be any negative values? – M Oehm Oct 08 '17 at 15:04
  • (Your `DEG` macro should probably have parentheses around `(x)`, athough it doesn't matter in this case.) – M Oehm Oct 08 '17 at 15:05
  • 1
    Okay, this is off-topic since It's probably not the issue, but precomputing `180.0 / PI` just makes your code less clear to read. I assure you the compiler will turn the constant expression into it's final value. They are good at that. – StoryTeller - Unslander Monica Oct 08 '17 at 15:05
  • @MOehm I'm not sure what you are saying here(1st comment). I normalize the values so that if i get a negative reading, i add two pi to the value (to make it the same value but positive). – Joel Oct 08 '17 at 15:10
  • 2
    Yes, but the limits of your normalisation aren't 0 and 2·π, which would correspond to 0° and 360°, but −π and π, which corresponds to −180° and 180°. Say your angle is 3/2 π. It is "normalised" to −1/2 π. `fmod(−1/2 π, 2 π)` will yield −1/2 π, which is then converted to −90°. – M Oehm Oct 08 '17 at 15:14
  • @MOehm oh, yes, you are correct. I used this logic to determine which way was the fastest to turn (which is just -180->180). Thought it had a general use without thinking it through. If you'd like you can post an answer and i'll approve it for you. – Joel Oct 08 '17 at 15:17
  • 1
    Call me crazy, but I would much rather use an inlined function then a macro, in every case where it's possible. – Alexander Oct 08 '17 at 15:27

2 Answers2

1

π cannot be exactly represented as a float, so any mod-ding with fmod(currentRot,PI*2); or comparing if(currentRot > PI){ may fail to provide the expected results for edge cases.

Note that currentRot = currentRot + 2 * PI; is a (foat) + (double) and the conversion to float. The edges of this conversion are problem to avoid slight negative results. Best to avoid mixing float and double math for this sensitive conversion.


Even with the good comment of @M Oehm, the inexactness PI and mixing float/double math can result in negative currentRot.

Instead, convert radians to degrees first and then mod.

float RadianToDegree(float r);
  float d = (float)(PI/180.0) * r;
  d = fmodf(d, 360.0f);
  if (d < 0.0f) d += 360.0f;
  return d;
}

The result will be [0.0 - 360.0] inclusive.

Mod-ding by 360 can be expected to incur to no round-off error.

Use fmodf() with float and fmod() with double to avoid unnecessary conversions.


On a higher level note, the goal of "The allowed values should lie between 0-360" sacrifice precision. Consider a primary range of between -180 - +180.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
-1

Here:

if(currentRot > PI){        //normalize values       
  currentRot     = currentRot - 2 * PI;
}
if(currentRot < -PI){
  currentRot     = currentRot + 2 * PI;
}

you try to normalize to -π < currentRot < π, and then you do:

currentRot = fmod(currentRot,PI*2);

which "Returns the floating-point remainder of numer/denom" (ref), meaning that the sign of the result, will be the same.

This does not ensure that negative values are prohibited. I mean your range doesn't start from 0.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • well, this isn't an answer to the question really. Here you are just stating the obvious :/ – Joel Oct 08 '17 at 21:41