To extract the magnitude of a difference, use the abs()
function:
abs(x - y)
But what about extracting the sign of that difference? In the before times, Python2 had cmp()
:
cmp(20, 30) # -1
cmp(20, 20) # 0
cmp(20, 10) # 1
And it was beautiful, because it could be used to extract the sign of a single number too:
cmp(x, 0)
Unfortunately, cmp()
was removed in Python3. Not a problem. It's easy enough to implement by hand:
def cmp(a, b):
if a < b:
return -1
elif a == b:
return 0
else:
return 1
But this seems silly. Why take away the function in the first place? While researching the answer, I discovered that the Python3 release notes recommended their own implementation for cmp()
:
def cmp(a, b):
return (a > b) - (a < b)
Whoa. This implementation is elegant, but it's also bonkers. It performs subtraction between numbers, converts those results to booleans, and then performs another subtraction between boolean values! And that boolean subtraction only works because, in Python, True
evaluates to 1
when cast to an integer, while False
evaluates to 0
. I can't believe this is the recommendation!
Getting back to the original question, it seems like cmp()
was removed to simplify and unify the various Python comparison functions already available, such as the operator
standard library.
And interestingly enough, attempts were made to include a comparable sign()
function in Python3. Check out this patch from 2007. However, disagreements on the correct treatment for +/-0 and +/-NAN inputs prevented the patch from being accepted. For example, the patch implementation for sign(0)
would return 1
, a stark difference from the 0
that cmp(0, 0)
would have returned.
This may seem unintuitive, but it matches the behaviour of copysign()
from the math
standard library. The copysign()
function takes the magnitude of its first input and combines it with the sign of the second, like so:
import math
math.copysign(10, -1) # -10
math.copysign(10, 1) # 10
math.copysign(10, -5) # -10
math.copysign(10, 5) # 10
Note that its behaviour around zeroes can be tricky:
import math
math.copysign(10, 0) # 10
math.copysign(10, -0) # 10
math.copysign(10, -0.0) # -10
This is probably so that it matches the implementation of copysign()
functions in other languages, including C++ and Javascript. Don't let it stump you!