10

I want to test if a number is positive or negative, especially also in the case of zero. IEEE-754 allows for -0.0, and it is implemented in Python.

The only workarounds I could find were:

def test_sign(x):
    return math.copysign(1, x) > 0

And maybe (probably takes longer to run):

def test_sign(x):
    math.atan2(x, -1)

I could not find a dedicated function anywhere in the usual libraries, did I overlook something?

Edit: (Why this was relevant)

This is not my current plan anymore, but when asking the question I tried to overload a function depending on whether an argument was positive or negative. Allowing the user to pass negative zeros would resolve the ambiguity what was meant for zero-valued input. And I think this may be of general interest for other use cases as well.

Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
quazgar
  • 4,304
  • 2
  • 29
  • 41
  • Not entirely I guess. @quazgar asks for a way to test the sign, not issues about it. – Willem Van Onsem Oct 11 '13 at 11:58
  • 3
    You should not treat -0 as negative, see [here](https://en.wikipedia.org/wiki/Signed_zero) for some explanations, or these references: [IEEE 754-1985](http://www.validlab.com/754R/standards/754.pdf) and [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://www.validlab.com/goldberg/paper.pdf). –  Oct 11 '13 at 12:01
  • @arbautjc that is true if the floating point numbers are indeed numbers. Perhaps quazgar wants to do something else with them, for instance pass a bit (the sign bit) through a method. In the end numbers, characters and objects are all 0's and 1's. – Willem Van Onsem Oct 11 '13 at 12:07
  • 1
    @arbautjc: Although the mathematical value of negative zero is zero, its sign bit is useful for some computations. See [this Stack Overflow answer](http://stackoverflow.com/questions/13544342/why-do-floating-points-have-signed-zeros/13544379#13544379) and the articles referenced in it. – Eric Postpischil Oct 13 '13 at 20:19
  • 1
    This doesn't seem like a duplicate to me; asking how to do this in Python is quite different from asking how to do it in Java. – charlescochran Aug 04 '23 at 15:12
  • @EricPostpischil it is not covered at all! You can't divide by zero in python, you will get a `ZeroDivisionError`. You can in Java. Completely different questions. – Hans Brende Aug 28 '23 at 21:35
  • 1
    @HansBrende: Okay, I voted to reopen. – Eric Postpischil Aug 28 '23 at 22:16

3 Answers3

2

You could use the binary representation:

import struct
def binary(num):
    return ''.join(bin(ord(c)).replace('0b', '').rjust(8, '0') for c in struct.pack('!f', num))

will return you the bit stream

The highest bit is the sign bit (0 is positive, 1 is negative)

However IEEE-754 also states that +0.0 == -0.0 == 0.0. Thus can't be sure that for instance -1.0+1.0 will for instance result in a positive or negative zero.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    +1 for the IEEE-754 note: The standard states that the three "values" are mathematically the same, which is important to consider. Since they are the same, why look for the sign... – David Božjak Oct 11 '13 at 12:52
  • 1
    @DavidBožjak Because it may allow a function to behave differently for positive and negative input (imagine plotting 1/x for example). And the sign of infinity may be different, depending on whether you divide by `+0.0` or '-0.0'. – quazgar Oct 11 '13 at 13:31
  • 1
    @quazgar: yeah. However some people state that `1/0.0` should be `NaN`. And most people will probably agree that it once starting to determine the sign of a certain number, something is wrong with your code. – Willem Van Onsem Oct 11 '13 at 14:31
  • Anyway, in Python, 1/0.0 throws a ZeroDivisionError - and some people say it's a sin. –  Oct 11 '13 at 14:38
2

Within the standard library of Python, your copysign approach is probably as good as it gets in terms of computational cost. It is by design that most things treat positive and negative zero the same except for instances like division by zero, where it makes a huge difference (literally).

If you have numpy, its signbit function does what you want.

import numpy as np


assert(np.signbit(-0.) == True)
assert(np.signbit(0.) == False)
assert(np.signbit(float('nan')) == False)
assert(np.signbit(float('-nan')) == True)
Homer512
  • 9,144
  • 2
  • 8
  • 25
  • I think this is the best answer as almost anyone who works with floats in python has numpy anyways. – julaine Aug 29 '23 at 08:01
1

You can use the struct module to test the bit pattern directly.

import struct

def is_neg_zero(n):
    return struct.pack('>d', n) == '\x80\x00\x00\x00\x00\x00\x00\x00'

def is_negative(n):
    return ord(struct.pack('>d', n)[0]) & 0x80 != 0
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622