12

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.

7

3 Answers 3

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.

4
  • 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... Commented Oct 11, 2013 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
    Commented Oct 11, 2013 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. Commented Oct 11, 2013 at 14:31
  • Anyway, in Python, 1/0.0 throws a ZeroDivisionError - and some people say it's a sin.
    – user1220978
    Commented Oct 11, 2013 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)
1
  • I think this is the best answer as almost anyone who works with floats in python has numpy anyways.
    – julaine
    Commented Aug 29, 2023 at 8: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

Not the answer you're looking for? Browse other questions tagged or ask your own question.