Skip to content

BUG: tanh returns unexpected values in special cases with complex numbers #21826

@steff456

Description

@steff456

Describe the issue:

Writing the spec for complex number support in the Array API we found that NumPy currently fails in 2 special cases. The behavior is inconsistent with the 2014 version C99.

The full list of special cases and specification is detailed in data-apis/array-api#458

cc @kgryte

Reproduce the code example:

import numpy as np
import math

def is_equal_float(x, y):
    """Test whether two floating-point numbers are equal with special consideration for zeros and NaNs.

    Parameters
    ----------
    x : float
        First input number.
    y : float
        Second input number.

    Returns
    -------
    bool
        Boolean indicating whether two floating-point numbers are equal.

    Examples
    --------
    >>> is_equal_float(0.0, -0.0)
    False
    >>> is_equal_float(-0.0, -0.0)
    True
    """
    # Handle +-0:
    if x == 0.0 and y == 0.0:
        return math.copysign(1.0, x) == math.copysign(1.0, y)

    # Handle NaNs:
    if x != x:
        return y != y

    # Everything else, including infinities:
    return x == y


def is_equal(x, y):
    """Test whether two complex numbers are equal with special consideration for zeros and NaNs.

    Parameters
    ----------
    x : complex
        First input number.
    y : complex
        Second input number.

    Returns
    -------
    bool
        Boolean indicating whether two complex numbers are equal.

    Examples
    --------
    >>> import numpy as np
    >>> is_equal(complex(np.nan, np.nan), complex(np.nan, np.nan))
    True
    """
    return is_equal_float(x.real, y.real) and is_equal_float(x.imag, y.imag)

def compare(v, e):
    actual = np.tanh(v) 
    print('Value: {value}'.format(value=str(v)))
    print('Actual: {actual}'.format(actual=str(actual)))
    print('Expected: {expected}'.format(expected=str(e)))
    print('Equal: {is_equal}'.format(is_equal=str(is_equal(actual, e))))
    print('\n')

# Case 1
v = complex(0.0, np.inf)
e = complex(0.0, np.nan)
compare(v, e) # returns `NaN + NaN j`; however, this does match old C99 behavior (see https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471)

# Case 2
v = complex(0.0, np.nan)
e = complex(0.0, np.nan)
compare(v, e) # returns `NaN + NaN j`; however, this does match old C99 behavior (see https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1892.htm#dr_471)

Error message:

Value: infj
Actual: (nan+nanj)
Expected: nanj
Equal: False

Value: nanj
Actual: (nan+nanj)
Expected: nanj
Equal: False

NumPy/Python version information:

v1.22.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions