Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

no sign() function ?

3 views
Skip to first unread message

Pierre-Alain Dorange

unread,
Dec 22, 2008, 5:18:00 AM12/22/08
to
I don't find any sign(x) function in the math library (return the sign
of the value).
I've read that math module is a wrapper to C math lib and that C math
lib has not sign(), so...

I've implement my own sign function of course (it's easy) but a standard
one in math would be better and could be faster.

How do you implement this or is there any other module with a sign()
function ?

--
Pierre-Alain Dorange <http://microwar.sourceforge.net/>

Ce message est sous licence Creative Commons "by-nc-sa-2.0"
<http://creativecommons.org/licenses/by-nc-sa/2.0/fr/>

Christian Heimes

unread,
Dec 22, 2008, 6:18:27 AM12/22/08
to pytho...@python.org
Pierre-Alain Dorange schrieb:

> I don't find any sign(x) function in the math library (return the sign
> of the value).
> I've read that math module is a wrapper to C math lib and that C math
> lib has not sign(), so...

Starting with Python 2.6 the math and cmath modules have a copysign
function.

> I've implement my own sign function of course (it's easy) but a standard
> one in math would be better and could be faster.

Sure? :) Are you aware that the IEEE 754 standard makes a difference
between the floats +0.0 and -0.0?

from math import atan2
def sign(x):
if x > 0 or (x == 0 and atan2(x, -1.) > 0.):
return 1
else:
return -1

Pierre-Alain Dorange

unread,
Dec 22, 2008, 6:31:44 AM12/22/08
to
Christian Heimes <li...@cheimes.de> wrote:

> Pierre-Alain Dorange schrieb:
> > I don't find any sign(x) function in the math library (return the sign
> > of the value).
> > I've read that math module is a wrapper to C math lib and that C math
> > lib has not sign(), so...
>
> Starting with Python 2.6 the math and cmath modules have a copysign
> function.

I'm using 2.5.2 at that time, but copysign() is fine.

with :
s=copysign(1.0,x)
it return the sign (-1.0, 0.0, +1.0)


> > I've implement my own sign function of course (it's easy) but a standard
> > one in math would be better and could be faster.
>
> Sure? :)

I was...

> Are you aware that the IEEE 754 standard makes a difference
> between the floats +0.0 and -0.0?
>
> from math import atan2
> def sign(x):
> if x > 0 or (x == 0 and atan2(x, -1.) > 0.):
> return 1
> else:
> return -1

Thanks
As my need is for a game and that i do not have IEEE real concern, i
would simply using my simple function (but not as accurate) :

def sign(x):
if x==0.0:
return 0.0
elif x>0.0:
return 1.0
else:
return -1.0

Robert Lehmann

unread,
Dec 22, 2008, 7:06:46 AM12/22/08
to
On Mon, 22 Dec 2008 12:31:44 +0100, Pierre-Alain Dorange wrote:

>> > I don't find any sign(x) function in the math library (return the
>> > sign of the value).
>> > I've read that math module is a wrapper to C math lib and that C math
>> > lib has not sign(), so...

[snip]


> As my need is for a game and that i do not have IEEE real concern, i
> would simply using my simple function (but not as accurate) :
>
> def sign(x):
> if x==0.0:
> return 0.0
> elif x>0.0:
> return 1.0
> else:
> return -1.0

I found this snippet to be quite succinct (even though being smart
*might* be wrong in programming)::

sign = lambda x:+(x > 0) or -(x < 0)

HTH,

--
Robert "Stargaming" Lehmann

Stephen Thorne

unread,
Dec 22, 2008, 7:20:58 AM12/22/08
to Pierre-Alain Dorange, pytho...@python.org
On 2008-12-22, Pierre-Alain Dorange wrote:
> def sign(x):
> if x==0.0:
> return 0.0
> elif x>0.0:
> return 1.0
> else:
> return -1.0

Isn't this approximately this? ::

def sign(x):
return float(cmp(x, 0))

Or if you don't want a float response::

def sign(x):
return cmp(x, 0)

--
Regards,
Stephen Thorne
Development Engineer
NetBox Blue - 1300 737 060

Pierre-Alain Dorange

unread,
Dec 22, 2008, 8:51:32 AM12/22/08
to
Stephen Thorne <ste...@thorne.id.au> wrote:

> > def sign(x):
> > if x==0.0:
> > return 0.0
> > elif x>0.0:
> > return 1.0
> > else:
> > return -1.0
>
> Isn't this approximately this? ::
>
> def sign(x):
> return float(cmp(x, 0))

Yes cmp() is probably the closest function to sign.

I'm new to python and here i discover at least 4 methods, i just make a
small script for timing those methods (100 000 times each on a set of 10
values).
I do not use timeit, i can't make it work easyly as it need a standalone
env for each test.

---- the test script --------------------
#!/usr/bin/env python

from math import atan2
import time


def sign_0(x):


if x==0.0:
return 0.0
elif x>0.0:
return 1.0
else:
return -1.0

def sign_1(x):


if x > 0 or (x == 0 and atan2(x, -1.) > 0.):
return 1
else:
return -1

def sign_2(x):
return cmp(x, 0)

sign_3 = lambda x:+(x > 0) or -(x < 0)

def main():
candidates=[1.1,0.0,-0.0,-1.2,2.4,5.6,-8.2,74.1,-23.4,7945.481]

startTime = time.clock()
for i in range(100000):
for value in candidates:
s=sign_0(value)
print "sign_0 : ",time.clock() - startTime

startTime = time.clock()
for i in range(100000):
for value in candidates:
s=sign_1(value)
print "sign_1 : ",time.clock() - startTime

startTime = time.clock()
for i in range(100000):
for value in candidates:
s=sign_2(value)
print "sign_2 : ",time.clock() - startTime

startTime = time.clock()
for i in range(100000):
for value in candidates:
s=sign_3(value)
print "sign_3 : ",time.clock() - startTime


if __name__ == '__main__' :
main()

---- the results -----------------------------
My config :
iMac (2,66 GHz intel dual core 2 duo)
MacOS X 10.5.5
Python 2.5.1

sign_0 = 0.4156 second (0%)
sign_1 = 0.5316 second (+28%)
sign_2 = 0.6515 second (+57%)
sign_3 = 0.5244 second (+26%)

---- conclusions -------------------------------

1/ python is fast
2/ method (0) is the fastest
3/ cmp method (2) is the slowest
4/ the precise one (IEEE 754) is also fast (1)

Istvan Albert

unread,
Dec 22, 2008, 9:33:12 AM12/22/08
to

> ---- conclusions -------------------------------

try testing on a large number of candidates that are all (or mostly)
positive or all (or mostly) negative and you'll see performance
numbers that are substantially different than the ones you report:

candidates = range(1000)

In general the function sign_1() is expected to be the fastest because
in most cases will detect the sign with the fewest operations, it only
visits the rest of the comparison when it hits the corner cases. Only
if you have lots of +/-0.0 cases will it be slower than the rest, due
to having to call an expensive operation.

i.

Pierre-Alain Dorange

unread,
Dec 22, 2008, 11:51:52 AM12/22/08
to
Istvan Albert <istvan...@gmail.com> wrote:

You're right.
On a range or a random list sign_1 is the fastest :

with :
candidates=[]
for i in range(1000):
candidates.append(1000.0*random.random()-500.0)

In my first candidate list, the two ZERO (-0.0 and +0.0) make sign_1
less productive because it call atan2().
With random number sign_1 is faster, just a bit faster, except if the
candidates contain some ZERO.

I also make other test, with a range(1000), sign_1 became really faster
: -41% (near twice faster than sign_0).
I then rewrote a sign_0 version testing first positive, then negative
and ZERO as the last test. I also made tests and return integer.
It make it faster +20% but not as fast as sign_1 for a range.

What is strange is that when testing with "range" list (0 1000) or (-500
+500), sign_1 is twice as fast as with a random generated list.
The only thing a saw is that range generate an int list and random a
float list...
So it seems sign_1 is really fastest with integer, but not with float

Range from -500 to +500
sign_0 : 0.38"
sign_1 : 0.27" (-40%)

Range from 0 to 1000
sign_0 : 0.32"
sign_1 : 0.25" (-22%)

Range from -1000 to 0
sign_0 : 0.46"
sign_1 : 0.30" (-35%)

1000 Random from -500 to +500
sign_0 : 0.37"
sign_1 : 0.42" (+13%)

Steven D'Aprano

unread,
Dec 22, 2008, 8:48:28 PM12/22/08
to
On Mon, 22 Dec 2008 14:51:32 +0100, Pierre-Alain Dorange wrote:

> I'm new to python and here i discover at least 4 methods, i just make a
> small script for timing those methods (100 000 times each on a set of 10
> values).
> I do not use timeit, i can't make it work easyly as it need a standalone
> env for each test.

But that's just what timeit does: it creates a standalone environment for
each test.


> ---- the test script -------------------- #!/usr/bin/env python
>
> from math import atan2
> import time
>
>
> def sign_0(x):
> if x==0.0:
> return 0.0
> elif x>0.0:
> return 1.0
> else:
> return -1.0
>
> def sign_1(x):
> if x > 0 or (x == 0 and atan2(x, -1.) > 0.):
> return 1
> else:
> return -1
>
> def sign_2(x):
> return cmp(x, 0)
>
> sign_3 = lambda x:+(x > 0) or -(x < 0)
>
> def main():
> candidates=[1.1,0.0,-0.0,-1.2,2.4,5.6,-8.2,74.1,-23.4,7945.481]
>
> startTime = time.clock()

time.clock() is low resolution on non-Windows systems. If you are using
Windows, this is okay, if you are not, you are shooting yourself in the
foot.


> for i in range(100000):
> for value in candidates:

Horribly inaccurate, because you are including the time to build a range
of 100,000 integers. In Python 3, that will only be a little bit
inaccurate, but in Python 2.x that will have a MAJOR effect -- possibly
swamping the actual function you are calling.


> s=sign_0(value)
> print "sign_0 : ",time.clock() - startTime

You are (possibly) including the time taken to print to std out in your
time.


> startTime = time.clock()
> for i in range(100000):
> for value in candidates:
> s=sign_1(value)
> print "sign_1 : ",time.clock() - startTime

Repeated code is poor coding practice. Write a function that takes a
function as argument, and call that:

def timer(function, candidates, count=100000):
loop = [None]*count
startTime = time.clock()
for i in loop:
for value in candidates:
s = function(value)
return time.clock() - startTime

But this is just duplicating what timeit already does. Trust me, learn to
use it, you won't be sorry. Here's a trick that took me a long time to
learn: instead of copying your functions into the setup code of timeit,
you can just import them.

This is what I would do (untested):


candidates=[1.1,0.0,-0.0,-1.2,2.4,5.6,-8.2,74.1,-23.4,7945.481]
setup = "from __main__ import sign_%d, candidates"
from timeit import Timer
template = "for x in candidates: s = sign_%d(x)"
for i in range(4):
t = min( Timer(template % i, setup % i).repeat() )
print "Testing sign_%d:" % i, t, "seconds"

> ---- conclusions -------------------------------

None of your conclusions are safe until you have re-tested with a better
timing method. That doesn't mean you are wrong, only that your reasons
for believing them are weak.

--
Steven

Arnaud Delobelle

unread,
Dec 23, 2008, 3:46:59 AM12/23/08
to
pdor...@pas-de-pub-merci.mac.com (Pierre-Alain Dorange) writes:

> def sign_0(x):
> if x==0.0:
> return 0.0
> elif x>0.0:
> return 1.0
> else:
> return -1.0
>

This might be slightly faster:

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

--
Arnaud

Pierre-Alain Dorange

unread,
Dec 23, 2008, 8:36:53 AM12/23/08
to
Steven D'Aprano <ste...@REMOVE.THIS.cybersource.com.au> wrote:

> But this is just duplicating what timeit already does. Trust me, learn to
> use it, you won't be sorry. Here's a trick that took me a long time to
> learn: instead of copying your functions into the setup code of timeit,
> you can just import them.

Thanks for the advise, i made the test using timeit and your very
interesting method to import... Now i know how to use timeit simply ;-)

New results on 1000 float values randomized from -500.0 to +500.0.
Each test is timeit(1000)

sign_0 : 0.375
sign_1 : 0.444 (+18%)
sign_2 : 0.661 (+76%)
sign_3 : 0.498 (+33%)

It seems it don't change the relative results between the methods.
Using timeit make measure accurate and remove print/range footprints.

I also try Arnaud's proposition, it make sign_0 just a little better
(-1%)

Christian Heimes

unread,
Dec 23, 2008, 9:59:36 AM12/23/08
to pytho...@python.org
All algorithm including my own suffer from one mistake. Nobody accounts
for NaN (not a number). You have to check for NaNs, too. NaNs have no
sign at all.

You could also try to do some fancy bit mask operation like

>>> ord(struct.pack("d", 0.)[7]) & 0x80
0
>>> ord(struct.pack("d", -0.)[7]) & 0x80
128

But you have to take care of little endian, big endian and mixd endian
IEEE 754 platforms, too. There are also platforms that don't have IEEE
754 floats at all ... Have fun :)

Christian

Mark Dickinson

unread,
Dec 23, 2008, 11:25:49 AM12/23/08
to
On Dec 23, 2:59 pm, Christian Heimes <li...@cheimes.de> wrote:
> All algorithm including my own suffer from one mistake. Nobody accounts
> for NaN (not a number). You have to check for NaNs, too. NaNs have no
> sign at all.

I think that's not quite true: NaNs have a sign; it's just not
accorded any particular meaning by IEEE 754---in particular,
the standard says nothing about the sign of a NaN result
for most arithmetic operations. However, the sign bit of
a NaN does play its usual role in operations like
copysign, abs, negation. For example, here's a Python
2.6 session on OS X, where the 'default' nan is negative,
in the sense that its sign bit is set:

>>> nan = float('nan')
>>> from math import copysign
>>> copysign(5.0, nan)
-5.0
>>> copysign(5.0, -nan)
5.0
>>> copysign(5.0, abs(nan))
5.0

Mark

ajaksu

unread,
Dec 23, 2008, 11:27:53 AM12/23/08
to
On Dec 22, 9:18 am, Christian Heimes <li...@cheimes.de> wrote:
> Sure? :) Are you aware that the IEEE 754 standard makes a difference
> between the floats +0.0 and -0.0?
>
> from math import atan2
> def sign(x):
>     if x > 0 or (x == 0 and atan2(x, -1.) > 0.):
>         return 1
>     else:
>         return -1

Is "x ** 0 > 0." instead of "atan2(x, -1.) > 0." unreliable across
platforms?


Mark Dickinson

unread,
Dec 23, 2008, 11:45:37 AM12/23/08
to
On Dec 23, 4:27 pm, ajaksu <aja...@gmail.com> wrote:
> Is "x ** 0 > 0." instead of "atan2(x, -1.) > 0." unreliable across
> platforms?

x**0 doesn't distinguish between x = -0.0 and x = 0.0.

I suspect you're confusing -0.0**0.0 with (-0.0)**0.0.

Mark

ajaksu

unread,
Dec 23, 2008, 5:42:53 PM12/23/08
to

Yes, fooled me :)

Thanks for the heads-up!

Daniel

Steven D'Aprano

unread,
Dec 24, 2008, 2:19:48 AM12/24/08
to
On Tue, 23 Dec 2008 14:36:53 +0100, Pierre-Alain Dorange wrote:

> Steven D'Aprano <ste...@REMOVE.THIS.cybersource.com.au> wrote:
>
>> But this is just duplicating what timeit already does. Trust me, learn
>> to use it, you won't be sorry. Here's a trick that took me a long time
>> to learn: instead of copying your functions into the setup code of
>> timeit, you can just import them.
>
> Thanks for the advise, i made the test using timeit and your very
> interesting method to import... Now i know how to use timeit simply ;-)
>
> New results on 1000 float values randomized from -500.0 to +500.0. Each
> test is timeit(1000)
>
> sign_0 : 0.375
> sign_1 : 0.444 (+18%)
> sign_2 : 0.661 (+76%)
> sign_3 : 0.498 (+33%)


Looking at those results, and remembering that each time is for one
million iterations of one thousand calls each, I'd say that there's so
little difference in speed between them, that you should choose whichever
function is easier to understand. At least until you profile your
application and discover that the sign() function is the bottleneck
keeping your program slow.

--
Steven

Pierre-Alain Dorange

unread,
Dec 24, 2008, 2:59:16 AM12/24/08
to
Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> wrote:

> > sign_0 : 0.375
> > sign_1 : 0.444 (+18%)
> > sign_2 : 0.661 (+76%)
> > sign_3 : 0.498 (+33%)
>
>
> Looking at those results, and remembering that each time is for one
> million iterations of one thousand calls each,

one million iteration only, that's enough but yes indeed this function
is fast.

> I'd say that there's so
> little difference in speed between them, that you should choose whichever
> function is easier to understand.

Yes, you're right. I just made those test for pure intellectual reason.

For me sign_0 is the simplest one to understood.
So in the domain of my little arcade game, this is what i'll use.
I don't need the accuraccy of sign_1, because in the application i just
need to know if the target is right or left or if the speed direction is
right or left.

> At least until you profile your
> application and discover that the sign() function is the bottleneck
> keeping your program slow.

In each frame i'll have to use about 10 to 20 sign() call, so it'll not
be the bottleneck.

ajaksu

unread,
Dec 24, 2008, 10:57:40 AM12/24/08
to
On Dec 24, 5:59 am, pdora...@pas-de-pub-merci.mac.com (Pierre-Alain

Dorange) wrote:
>
> For me sign_0 is the simplest one to understood.
> So in the domain of my little arcade game, this is what i'll use.
> I don't need the accuraccy of sign_1, because in the application i just
> need to know if the target is right or left or if the speed direction is
> right or left.

If there is a chance of getting bogus input to sign_0 (like a list,
None or other non-numeric types) it might be worth to add a quick
check like:

def sign_0(x):
x + 0
if x==0.0:
...

0 new messages