7

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
  • 27,124
  • 9
  • 79
  • 112
quazgar
  • 4,029
  • 2
  • 28
  • 39
  • 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
  • I have voted for this as a duplicate. Although the proposed original asks a different question, this one is completely covered in the accepted answer. – Eric Postpischil Oct 13 '13 at 20:17
  • 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

2 Answers2

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
  • 397,926
  • 29
  • 362
  • 485
  • 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
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
  • 286,393
  • 40
  • 379
  • 604