Sign Function in Python: sign/signum/sgn, copysign
In Python, the built-in functions and standard libraries do not provide the sign function, i.e., the function that returns 1
, -1
, or 0
depending on the sign of a number. If you need such a function, you could use numpy.sign()
from NumPy or define your own function.
The sign function (sign, signum, sgn)
The sign function returns 1
for positive numbers, -1
for negative numbers, and 0
for zero. You can get the sign of a number using this function.
As of version 3.11, Python does not include the sign function in its built-in functions or standard libraries.
See Stack Overflow and Python Bug Tracker below for reasons why the sign function is not provided in Python.
- language design - Why doesn't Python have a sign function? - Stack Overflow
- Issue 1640: Enhancements for mathmodule - Python tracker
The developers chose to implement copysign()
in the math
module instead of sign()
so that users can decide what value should be returned in edge cases such as +0.0
, -0.0
, NaN
, and -NaN
.
For more information on signed zero, see below.
In Python, the floating-point data type float
can represent a negative zero, denoted as -0.0
.
There is no distinction between positive and negative zeros in the integer type int
; -0
is interpreted as 0
.
print(0)
# 0
print(-0)
# 0
print(0 == -0)
# True
print(0 is -0)
# True
In contrast, float
treats 0.0
and -0.0
as equivalent, yet distinct objects.
print(0.0)
# 0.0
print(-0.0)
# -0.0
print(0.0 == -0.0)
# True
print(0.0 is -0.0)
# False
numpy.sign()
NumPy provides numpy.sign()
. If a sign function is required, numpy.sign()
is a convenient choice.
numpy.sign()
returns an int
when given an int
input, and a float
when given a float
input.
import numpy as np
print(np.sign(100))
# 1
print(np.sign(-100))
# -1
print(type(np.sign(100)))
# <class 'numpy.int64'>
print(np.sign(1.23))
# 1.0
print(np.sign(-1.23))
# -1.0
print(type(np.sign(1.23)))
# <class 'numpy.float64'>
It returns zero for all zeros, including the negative zero -0.0
.
print(np.sign(0))
# 0
print(np.sign(0.0))
# 0.0
print(np.sign(-0.0))
# 0.0
It returns NaN
for NaN
.
print(np.sign(float('nan')))
# nan
print(np.sign(float('-nan')))
# nan
numpy.sign()
works with both scalar values and NumPy arrays (numpy.ndarray
). Refer to the following article for more details.
Define the sign function in Python
With comparison operators
In Python, comparison operations typically return True
and False
. Since bool
is a subclass of int
, True
can be treated as 1
and False
as 0
.
print(100 > 0)
# True
print(True - False)
# 1
print(False - True)
# -1
print(False - False)
# 0
Based on this, you can define the sign function as follows:
def my_sign(x):
return (x > 0) - (x < 0)
The result is always an integer (int
).
print(my_sign(100))
# 1
print(my_sign(-100))
# -1
print(type(my_sign(100)))
# <class 'int'>
print(my_sign(1.23))
# 1
print(my_sign(-1.23))
# -1
print(type(my_sign(-1.23)))
# <class 'int'>
The function returns 0
for both zero and NaN
, regardless of their signs.
print(my_sign(0))
# 0
print(my_sign(0.0))
# 0
print(my_sign(-0.0))
# 0
print(my_sign(float('nan')))
# 0
print(my_sign(float('-nan')))
# 0
This function does not support complex numbers.
# print(my_sign(3 + 4j))
# TypeError: '>' not supported between instances of 'complex' and 'int'
Note that some implementations of special methods such as __lt__
may return values other than True
or False
.
By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false. 3. Data model -
__lt__
— Python 3.11.3 documentation
When using built-in types like int
and float
, you don't need to worry about this, but caution is required when dealing with user-defined classes.
With abs()
You can also define the sign function using the built-in abs()
function, which returns the absolute value of a number.
Here, the conditional expression is employed.
def my_sign_with_abs(x):
return 0.0 if abs(x) == 0 else x / abs(x)
This example returns a floating-point number (float
) because it uses division /
to support complex numbers, as described below.
print(my_sign_with_abs(100))
# 1.0
print(my_sign_with_abs(-100))
# -1.0
print(type(my_sign_with_abs(100)))
# <class 'float'>
print(my_sign_with_abs(1.23))
# 1.0
print(my_sign_with_abs(-1.23))
# -1.0
print(type(my_sign_with_abs(1.23)))
# <class 'float'>
It returns 0.0
for zero and NaN
for NaN
.
print(my_sign_with_abs(0))
# 0.0
print(my_sign_with_abs(0.0))
# 0.0
print(my_sign_with_abs(-0.0))
# 0.0
print(my_sign_with_abs(float('nan')))
# nan
print(my_sign_with_abs(float('-nan')))
# nan
Since abs()
returns absolute values (distances on the complex plane) for complex numbers as well, this implementation also supports complex numbers.
print(abs(3 + 4j))
# 5.0
print(my_sign_with_abs(3 + 4j))
# (0.6+0.8j)
It returns a unit vector pointing in the same direction as the original complex number.
The signum of a given complex number z is the point on the unit circle of the complex plane that is nearest to z. Sign function - Complex signum - Wikipedia
math.copysign()
While Python does not provide a sign()
function, it does include the copysign()
function in its math
module.
math.copysign()
returns a float
with the absolute value of its first argument and the sign of its second argument.
import math
print(math.copysign(123, -100))
# -123.0
print(math.copysign(123.0, -100.0))
# -123.0
You can create a function similar to the sign function using math.copysign()
. Note that its behavior differs from the typical sign function, as it doesn't return 0
for 0
, as described below.
def my_sign_with_copysign(x):
return int(math.copysign(1, x))
This example uses int()
to always return an int
.
print(my_sign_with_copysign(100))
# 1
print(my_sign_with_copysign(-100))
# -1
print(type(my_sign_with_copysign(100)))
# <class 'int'>
print(my_sign_with_copysign(1.23))
# 1
print(my_sign_with_copysign(-1.23))
# -1
print(type(my_sign_with_copysign(1.23)))
# <class 'int'>
It returns 1
for an integer zero, and 1
or -1
for a floating-point zero, depending on its sign.
print(my_sign_with_copysign(0))
# 1
print(my_sign_with_copysign(0.0))
# 1
print(my_sign_with_copysign(-0.0))
# -1
It returns 1
or -1
for NaN
as well, depending on its sign.
print(my_sign_with_copysign(float('nan')))
# 1
print(my_sign_with_copysign(float('-nan')))
# -1
Since math.copysign()
does not support complex numbers, this function does not support complex numbers.
# print(math.copysign(1, 3 + 4j))
# TypeError: can't convert complex to float
# print(my_sign_with_copysign(3 + 4j))
# TypeError: can't convert complex to float