views:

607

answers:

5

I can't understand why python has not 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 python designer(s) decided to leave the sign() function out of the language? Why in the heck bothering about 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?

+4  A: 

cmp is definitely necessary. It can work on anything, not just numbers. If I have Person A and Person B, I can't compare them to see which is greater by doing sign(A - B). Subtraction makes no sense for a Person, but comparison might - for example, I can sort by name in alphabetical order. Also cmp can show equality, while < cannot.

I see why you would want sign, but you can just put this line at the top of your code:

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

and all your problems are solved. Why did the Python devs include copysign and not sign in math? That is a better question for the Python dev list.

Claudiu
Thanks for your answer, even though you don't add anything that I already stated in the question (maybe more clear). About the recommendation to go to the python dev list, wouldn't this argument work for all the questions on this site? Just close stackoverflow and ask every question to the relevant topic dev or user mailing list!
Davide
I agree with that sentiment generally. However, in this particular case, you are literally asking: "Why did the Python devs not include sign()?" We can only guess, and people have already done so, but there is no way to be certain unless: 1. a Python dev is on SO and answers your question, or 2. you ask the dev list.
Claudiu
Note that your definition of sign is uncorrect, in that it leaves out the case in which `x == 0` (especially relevant for floats, since you can have IEEE 754 negative zeros, for which your definition gives `sign(-0)==1`)
Davide
Good point! That adds some weight as to why sign should be a built-in. Personally I wouldn't mind. But I can't tell you why it's not already there.
Claudiu
+1  A: 

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.

Antoine P.
Well, I'd buy this only if `abs()` was left out also. `sign()` and `abs()` are often used together, `sign()` is the most useful of the two (IMO), and none is remotely hard or tedious to implement (even though it's error prone, see how this answer get it wrong: http://stackoverflow.com/questions/1986152/why-python-doesnt-have-a-sign-function/1986343#1986343 )
Davide
The thing is that the numerical result of `sign()` itself is rarely useful. What you do most of the time is take different code paths based on whether a variable is positive or negative, and in that case it's more readable to write the condition explicitly.
Antoine P.
abs() is used much more often than a sign() would be. And I pointed you to the NumPy tracker which shows how hard sign() can be to implement. What should sign(-3+4j) be? While abs(-3+4j) is 5.0. You make assertions that sign() and abs() are often seen together. The C standard library doesn't have a 'sign' function, so where are you getting your data?
Andrew Dalke
+12  A: 

"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 signbit(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 arond: 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.

Andrew Dalke
Well, I don't if that will make you happy, but Python 3 has neither `cmp()` nor `sign()` :-)
Antoine P.
writing a good sign() function that would work properly with IEEE 754 is not trivial. This would be a good point to include it in the language, rather than leaving it out, even though I didn't elaborate this point in the question
Davide
Your comment about how "if you want the sort to be stable" means you also don't know how stable sort works. Your statement that copysign and sign are equivalent show that you didn't know much about IEEE 754 math before this post. Should Python implement all of the 754 math functions in core? What should it do for non-C99 compilers? Non-754 platforms? "isnonnegative" and "isnonpositive" are also useful functions. Should Python also include those? abs(x) defers to x.__abs__(), so should sign(x) defer to x.__sign__() ? There's little demand or need for it, so why should it be stuck into core?
Andrew Dalke
+8  A: 

EDIT:

Indeed there was a patch which included sign() in math, but it wasn't accepted, because they didn't agree on what it should return in all the edge cases (+/-0, +/-nan, etc)

So they decided to implement only copysign, which (although more verbose) can be used to delegate to the end user the desired behavior for edge cases - which sometimes might require the call to cmp(x,0).

Thanks to everybody who, with sarcasm and unnecessary flaming, helped to make stackoverflow a worse place.


I don't know why it's not a built-in, but I have some thoughts.

copysign(x,y):
Return x with the sign of y.

Most importantly, copysign is a superset of sign! Calling copysign with x=1 is the same as a sign function. So you could just use copysign and forget about it.

>>> math.copysign(1, -4)
-1.0
>>> math.copysign(1, 3)
1.0

If you get sick of passing two whole arguments, you can implement sign this way, and it will still be compatible with the IEEE stuff mentioned by others:

>>> sign = functools.partial(math.copysign, 1) # either of these
>>> sign = lambda x: math.copysign(1, x) # two will work
>>> sign(-4)
-1.0
>>> sign(3)
1.0

Secondly, usually when you want the sign of something, you just end up multiplying it with another value. And of course that's basically what copysign does.

So, instead of:

s = sign(a)
b = b * s

You can just do:

b = copysign(b, a)

And yes, I'm surprised you've been using Python for 7 years and think cmp could be so easily removed and replaced by sign! Have you never implemented a class with a __cmp__ method? Have you never called cmp and specified a custom comparator function?

In summary, I've found myself wanting a sign function too, but copysign with the first argument being 1 will work just fine. I disagree that sign would be more useful than copysign, as I've shown that it's merely a subset of the same functionality.

FogleBird
Using `[int(copysign(1, zero)) for zero in (0, 0.0, -0.0)]` gives `[1, 1, -1]`. That should have been `[0, 0, 0]` according to http://en.wikipedia.org/wiki/Sign_function
You have the order wrong. [int(copysign(zero, 1)) for zero in (0, 0.0, -0.0)] gives the [0,0,0] you're looking for.
Andrew Dalke
A: 

StackOverflow is really the wrong place for this. You already knew that it is possible to argue over whether sign() was built-in to Python, so you aren't trying to find out arguments about whether sign() should be built in. You already know how to write your own version of sign(). You just came here for a soap box.

All that said, why don't you send email to the Python developers' mailing list and propose adding sign() to Python's math module? If it already has copysign() and Python already has abs() it seems likely that you might be able to get this added.

I doubt they would be willing to add sign() to the core language, but I think they might be willing to add it to math.

And as for me, I don't think one of these is that much harder than the other:

from math import sign


import math
def sign(x):
    return math.copysign(1.0, x)

P.S. To some extent, I feel your pain. I feel almost the same way you do about Python having a builtin struct feature. But it's really easy to write your own, and that's what I do.

http://stackoverflow.com/questions/1878710/struct-objects-in-python

steveha