Include `math.sign`

Yes, and underflow cannot be captured even if the sign function tries alternatives only if the conversion to float has failed.

If one wants a type-generic function, I think that it should go to the type, maybe as a method or using a dunder (like __builtins__.abs(x) just calls type(x).__abs__(x)). Some people here wanted sign(x) * abs(x) to be the same as x. Integers, floats, complex numbers, fractionals and decimals have been discussed. What about arrays? Numpy arrays allow for that

>>> x = np.array([1,-2,3])
>>> abs(x)      # Numpy already provides an __abs__ dunder!
array([1, 2, 3])
>>> np.abs(x)
array([1, 2, 3])
>>> np.sign(x)
array([ 1, -1,  1])
>>> np.sign(x) * np.abs(x)
array([ 1, -2,  3])

None of the supposedly general implementations that is based on __lt__, __gt__ and __eq__ allows for this.

Instead, those implementations then also check for dubious types where x>0 and x<0 could be both true, like @acolesnicov’s “Gold Edition” code does. Since one of the goals of this forum is to allow people to learn about Python (including CPython’s C-API), I also want to comment a bit about the “The “Ternary Logic” Challenge”, specifically lines 128-132 in his signum.cpp file:

    gt = PyObject_RichCompareBool(x, Py_zero, Py_GT) + 1; /* 0: Error; 1: False; 2: True */
    stat_idx = gt;

    lt = PyObject_RichCompareBool(x, Py_zero, Py_LT) + 1;
    res = (long)gt - lt; /* Result, if nothing special */

This code is buggy. If PyObject_RichCompareBool(x, Py_zero, Py_GT) returns -1, an error occurred and an Exception has been set. Usually, you want to cleanup and return NULLin this case. If you want to proceed and call again into the C-API you have to clear the exception first. The code as is could easily lead to a crash.

After handling those cases correctly, the code is something like

    int gt = PyObject_RichCompareBool(x, Py_zero, Py_GT);
    if (gt<0) return NULL;
    int lt = PyObject_RichCompareBool(x, Py_zero, Py_LT);
    if (lt<0) return NULL;
    int eq = PyObject_RichCompareBool(x, Py_zero, Py_EQ);
    if (eq<0) return NULL;
    // The following if now superseeds the switch statement and the bitshift operations...
    if (gt+lt+eq==1) {
        return PyLong_FromLong(gt-lt);
    }
    else {
        // Error case (including nan)
   }