a % b == b when a is a very small negative number
Tim Peters
tim.one at comcast.net
Sun Nov 24 12:51:42 EST 2002
[Lee Harr]
> 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.
>
> With that input, self.direction == 2*PI.
Numerically, that's unfortunately so. Python guarantees that x%y has the
same sign as y, so Python can't return -1e-16 in this case (its sign differs
from the sign of 2*PI). So it adds 2*PI to -1e16, and numerically (although
not mathematically) that's exactly equal to 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)
Thatwouldn't help your original case, since direction was < 0 there.
Depending on the accuracy requirements of your application, replacing the
guard with
if abs(direction) < 1e-5:
may be appropriate.
> Is this not a proper use of the % operator?
> Should not a % b always be less than b?
Mathematically, yes. Numerically, something has to lose: it's not always
possible for all of these to be true numerically:
1. sign(x%y) == sign(y)
2. abs(x%y) < abs(y)
3. divmod(x, y)[0] * y + x % y is very close to x
Python's % applied to floats preserves #1 and #3 at the expense of #2 in
some cases. If you want #2 more than #1, use math.fmod instead:
>>> math.fmod(-1e-16, 2*math.pi)
-9.9999999999999998e-017
>>>
More information about the Python-list
mailing list