[python] Why doesn't Python have a sign function?

I can't understand why Python doesn't have a sign function. It has an abs builtin (which I consider sign's sister), but no sign.

In python 2.6 there is even a copysign function (in math), but no sign. Why bother to write a copysign(x,y) when you could just write a sign and then get the copysign directly from abs(x) * sign(y)? The latter would be much more clear: x with the sign of y, whereas with copysign you have to remember if it's x with the sign of y or y with the sign of x!

Obviously sign(x) does not provide anything more than cmp(x,0), but it would be much more readable that this too (and for a greatly readable language like python, this would have been a big plus).

If I were a python designer, I would been the other way arond: no cmp builtin, but a sign. When you need cmp(x,y), you could just do a sign(x-y) (or, even better for non-numerical stuff, just a x>y - of course this should have required sorted accepting a boolean instead of an integer comparator). This would also be more clear: positive when x>y (whereas with cmp you have to remember the convention positive when the first is bigger, but it could be the other way around). Of course cmp makes sense in its own for other reasons (e.g. when sorting non-numerical things, or if you want the sort to be stable, which is not possible using with simply a boolean)

So, the question is: why did the Python designer(s) decide to leave the sign function out of the language? Why the heck bother with copysign and not its parent sign?

Am I missing something?

EDIT - after Peter Hansen comment. Fair enough that you didn't use it, but you didn't say what you use python for. In 7 years that I use python, I needed it countless times, and the last is the straw that broke the camel's back!

Yes, you can pass cmp around, but 90% of the times that I needed to pass it was in an idiom like lambda x,y: cmp(score(x),score(y)) that would have worked with sign just fine.

Finally, I hope you agree that sign would be more useful than copysign, so even if I bought your view, why bother about defining that in math, instead of sign? How can copysign be so much useful than sign?

This question is related to python language-design

The answer is


In Python 2, cmp() returns an integer: there's no requirement that the result be -1, 0, or 1, so sign(x) is not the same as cmp(x,0).

In Python 3, cmp() has been removed in favor of rich comparison. For cmp(), Python 3 suggests this:

def cmp(a, b):
    return (a > b) - (a < b)

which is fine for cmp(), but again can't be used for sign() because the comparison operators need not return booleans.

To deal with this possibility, the comparison results must be coerced to booleans:

 def sign(x):
    return bool(x > 0) - bool(x < 0)

This works for any type which is totally ordered (including special values like NaN or infinities).


Another one liner for sign()

sign = lambda x: (1, -1)[x<0]

If you want it to return 0 for x = 0:

sign = lambda x: x and (1, -1)[x<0]

Try running this, where x is any number

int_sign = bool(x > 0) - bool(x < 0)

The coercion to bool() handles the possibility that the comparison operator doesn't return a boolean.


Since cmp has been removed, you can get the same functionality with

def cmp(a, b):
    return (a > b) - (a < b)

def sign(a):
    return (a > 0) - (a < 0)

It works for float, int and even Fraction. In the case of float, notice sign(float("nan")) is zero.

Python doesn't require that comparisons return a boolean, and so coercing the comparisons to bool() protects against allowable, but uncommon implementation:

def sign(a):
    return bool(a > 0) - bool(a < 0)

"copysign" is defined by IEEE 754, and part of the C99 specification. That's why it's in Python. The function cannot be implemented in full by abs(x) * sign(y) because of how it's supposed to handle NaN values.

>>> import math
>>> math.copysign(1, float("nan"))
1.0
>>> math.copysign(1, float("-nan"))
-1.0
>>> math.copysign(float("nan"), 1)
nan
>>> math.copysign(float("nan"), -1)
nan
>>> float("nan") * -1
nan
>>> float("nan") * 1
nan
>>> 

That makes copysign() a more useful function than sign().

As to specific reasons why IEEE's signbit(x) is not available in standard Python, I don't know. I can make assumptions, but it would be guessing.

The math module itself uses copysign(1, x) as a way to check if x is negative or non-negative. For most cases dealing with mathematical functions that seems more useful than having a sign(x) which returns 1, 0, or -1 because there's one less case to consider. For example, the following is from Python's math module:

static double
m_atan2(double y, double x)
{
        if (Py_IS_NAN(x) || Py_IS_NAN(y))
                return Py_NAN;
        if (Py_IS_INFINITY(y)) {
                if (Py_IS_INFINITY(x)) {
                        if (copysign(1., x) == 1.)
                                /* atan2(+-inf, +inf) == +-pi/4 */
                                return copysign(0.25*Py_MATH_PI, y);
                        else
                                /* atan2(+-inf, -inf) == +-pi*3/4 */
                                return copysign(0.75*Py_MATH_PI, y);
                }
                /* atan2(+-inf, x) == +-pi/2 for finite x */
                return copysign(0.5*Py_MATH_PI, y);

There you can clearly see that copysign() is a more effective function than a three-valued sign() function.

You wrote:

If I were a python designer, I would been the other way around: no cmp() builtin, but a sign()

That means you don't know that cmp() is used for things besides numbers. cmp("This", "That") cannot be implemented with a sign() function.

Edit to collate my additional answers elsewhere:

You base your justifications on how abs() and sign() are often seen together. As the C standard library does not contain a 'sign(x)' function of any sort, I don't know how you justify your views. There's an abs(int) and fabs(double) and fabsf(float) and fabsl(long) but no mention of sign. There is "copysign()" and "signbit()" but those only apply to IEEE 754 numbers.

With complex numbers, what would sign(-3+4j) return in Python, were it to be implemented? abs(-3+4j) return 5.0. That's a clear example of how abs() can be used in places where sign() makes no sense.

Suppose sign(x) were added to Python, as a complement to abs(x). If 'x' is an instance of a user-defined class which implements the __abs__(self) method then abs(x) will call x.__abs__(). In order to work correctly, to handle abs(x) in the same way then Python will have to gain a sign(x) slot.

This is excessive for a relatively unneeded function. Besides, why should sign(x) exist and nonnegative(x) and nonpositive(x) not exist? My snippet from Python's math module implementation shows how copybit(x, y) can be used to implement nonnegative(), which a simple sign(x) cannot do.

Python should support have better support for IEEE 754/C99 math function. That would add a signbit(x) function, which would do what you want in the case of floats. It would not work for integers or complex numbers, much less strings, and it wouldn't have the name you are looking for.

You ask "why", and the answer is "sign(x) isn't useful." You assert that it is useful. Yet your comments show that you do not know enough to be able to make that assertion, which means you would have to show convincing evidence of its need. Saying that NumPy implements it is not convincing enough. You would need to show cases of how existing code would be improved with a sign function.

And that it outside the scope of StackOverflow. Take it instead to one of the Python lists.


It just doesn't.

The best way to fix this is:

sign = lambda x: bool(x > 0) - bool(x < 0)

The reason "sign" is not included is that if we included every useful one-liner in the list of built-in functions, Python wouldn't be easy and practical to work with anymore. If you use this function so often then why don't you do factor it out yourself? It's not like it's remotely hard or even tedious to do so.


Yes a correct sign() function should be at least in the math module - as it is in numpy. Because one frequently needs it for math oriented code.

But math.copysign() is also useful independently.

cmp() and obj.__cmp__() ... have generally high importance independently. Not just for math oriented code. Consider comparing/sorting tuples, date objects, ...

The dev arguments at http://bugs.python.org/issue1640 regarding the omission of math.sign() are odd, because:

  • There is no separate -NaN
  • sign(nan) == nan without worry (like exp(nan) )
  • sign(-0.0) == sign(0.0) == 0 without worry
  • sign(-inf) == -1 without worry

-- as it is in numpy


numpy has a sign function, and gives you a bonus of other functions as well. So:

import numpy as np
x = np.sign(y)

Just be careful that the result is a numpy.float64:

>>> type(np.sign(1.0))
<type 'numpy.float64'>

For things like json, this matters, as json does not know how to serialize numpy.float64 types. In that case, you could do:

float(np.sign(y))

to get a regular float.


Only correct answer compliant with the Wikipedia definition

The definition on Wikipedia reads:

sign definition

Hence,

sign = lambda x: -1 if x < 0 else (1 if x > 0 else (0 if x == 0 else NaN))

Which for all intents and purposes may be simplified to:

sign = lambda x: -1 if x < 0 else (1 if x > 0 else 0)

This function definition executes fast and yields guaranteed correct results for 0, 0.0, -0.0, -4 and 5 (see comments to other incorrect answers).

Note that zero (0) is neither positive nor negative.


You dont need one, you can just use:

if not number == 0:
    sig = number/abs(number)
else:
    sig = 0

Or create a function as described by others:

sign = lambda x: bool(x > 0) - bool(x < 0)

def sign(x):
    return bool(x > 0) - bool(x < 0)