a % b == b when a is a very small negative number

Bengt Richter bokr at oz.net
Sun Nov 24 14:45:20 EST 2002


On Sun, 24 Nov 2002 16:15:08 -0000, Lee Harr <missive at frontiernet.net> wrote:

>
>I have some code which is storing the direction an object
>is moving, and I want to keep that value: 0 <= direction < 2*PI
>
>So I do something like this:
>
>class Directed:
>    def setDirection(self, direction):
>        self.direction = direction % (2*PI)  # PI = math.pi
>
>This works fine until direction is some very very small negative number,
>like -0.1e-16.
>
Does this do what you want?

 >>> class Directed:
 ...      import math
 ...      TWOPI = 2.0*math.pi
 ...      def setDirection(self, direction):
 ...          turns = direction/self.TWOPI
 ...          turns -= int(turns)   # effect is modulo TWOPI on absolute value of angle
 ...          self.direction = turns*self.TWOPI
 ...
 >>> d = Directed()
 >>> d.setDirection(-0.1e-16)
 >>> d.direction
 -1.0000000000000001e-017
 >>> d.setDirection(+0.1e-16)
 >>> d.direction
 1.0000000000000001e-017
 >>> d.setDirection(Directed.TWOPI)
 >>> d.direction
 0.0
 >>> d.setDirection(Directed.TWOPI+0.1e-16)
 >>> d.direction
 0.0
 >>> d.setDirection(Directed.TWOPI-0.1e-16)
 >>> d.direction
 0.0
 >>> d.setDirection(Directed.TWOPI+0.1e-15)
 >>> d.direction
 0.0
 >>> d.setDirection(Directed.TWOPI+0.1e-14)
 >>> d.direction
 1.3951473992034527e-015
 >>> d.setDirection(Directed.TWOPI-0.1e-15)
 >>> d.direction
 0.0
 >>> d.setDirection(Directed.TWOPI-0.1e-14)
 >>> d.direction
 6.2831853071795853
 >>> d.setDirection(-Directed.TWOPI-0.1e-15)
 >>> d.direction
 0.0
 >>> d.setDirection(-Directed.TWOPI-0.1e-14)
 >>> d.direction
 -1.3951473992034527e-015
 >>> d.setDirection(-Directed.TWOPI+0.1e-14)
 >>> d.direction
 -6.2831853071795853 

>With that input, self.direction == 2*PI.
>
>
>What I do right now to work around this is take the modulus twice:
>
>    def setDirection(self, direction):
>        self.direction = direction % (2*PI) % (2*PI)  # weird, but works
>
>Though thinking about it now, it might be better to simply
>check for a very small number:
>
>    def setDirection(self, direction):
>        if direction> 0 and direction < 0.00001:
>            self.direction = 0
>        else:
>            self.direction = direction % (2*PI)
>
>
>Is this not a proper use of the % operator?
>Should not a % b always be less than b?
>

>From th docs
"""
The modulo operator always yields a result with the same sign as
its second operand (or zero); the absolute value of the result
is strictly smaller than the second operand.
"""

I don't know what you are doing, but remember that you can't represent
2*PI exactly unless you invent a scaled unit for it. E.g. you could define
exactly one (i.e., 1.0) turn to be equal to exactly 2*pi mathematical radians.
Then if you have an angle in turn units, angle-int(angle) will do
an effective modulo 2*PI on the absolute value of your angle and leave
the sign intact. If you want to go even further, you might want to represent
turns as rationals instead of floats, or define a fractional turn unit suitable
for your application. Of course you always have to multiply by a scale factor before
calling sin or cos etc, but you can avoid accumulating angular errors if your
problem steps in exact increments. OTOH the error in doubles is small enough
for most practical problems.

Regards,
Bengt Richter



More information about the Python-list mailing list