# Include \`math.sign\`

**URL:** <https://discuss.python.org/t/include-math-sign/45404>\
**Category:** Ideas\
**Created:** [February 8, 2024, 6:51pm UTC](https://discuss.python.org/t/include-math-sign/45404 "2024-02-08T18:51:50Z")\
**Posts on this page:** 1\
**Showing post:** 67

<div class="post-metadata">

**Author:** ![jagerber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jagerber/32/9412_2.png) [@jagerber](https://discuss.python.org/u/jagerber)\
**Post date:** [February 10, 2024, 2:16pm UTC](https://discuss.python.org/t/include-math-sign/45404/67 "2024-02-10T14:16:04Z")

</div>

> [@mdickinson](#):
>
> One thing that seems to be largely missing from those previous discussions is example use-cases. It would be really helpful to have a body of examples of Python code that could potentially be improved by the existence of a `sign` function - those examples could then be used to help evaluate trade-offs between possible (re)solutions. Did you have particular use-cases in mind when you opened this discussion?

Good and helpful question. Yes, I did have a use case in mind when I opened this discussion. I’m working on a PyPi number formatting package (inspired by [your suggestion](https://discuss.python.org/t/new-format-specifiers-for-string-formatting-of-floats-with-si-and-iec-prefixes/26914/45)) which is almost an extension of what the python format-specification mini-language (FSML) can do for floats/decimals (almost because it does more, but there are a few things the python formatter does that were intentionally left out). In any case, currently the formatter has similar behavior to the `[+-]` sign symbol selection option of the python FSML.

I recently came to think harder about how my formatter behaves when it needs to format the sign of `0` and `nan`. I think it’s at least worth considering that the `+` format option combined with `0.0` returns `" 0.0"` instead of `+0.0`. For `nan` the current behavior is that `nan` is always formatetd to `"nan"` but when I started looking into this I learned that `nan` has a sign bit and started wondering if my formatter should include the sign on `nan` similar to how the `Decimal` object includes the sign. Anyways, my takeaway is that I should NOT include the sign, but I had to go through a lot of research, including my linked research on this topic, to come to that conclusion. A documented stdlib `sign` function could have short circuited this research I ended up doing.

So I do think that this case of formatting numbers is especially suited towards specifically wanting a ternary `sign` function. The package is also meant to have no third party dependencies so I also specifically don’t want e.g. `numpy`. An objection to my use case is that I’m going to have code that switches cases based on the output of `sign(x)` and that case-switching code is basically going to be as complex as the code for `sign` that is being proposed, so it may not even save lines of code in my case. Nonetheless, if `sign` were available I would use it for (my perception of) improved semantics/readability.

Your “point-in-polyhedron” example reminded me of [something similar](https://github.com/pyvista/pyvista/blob/release/0.43/pyvista/core/utilities/geometric_objects.py#L717) I was helping with about generating a solid mesh of a 3D sphere. The sphere is parametrized in polar coordinates and you need to detect when you’ve wrapped the polar angle to pi or azimuthal angle to 2 pi. This example is a little less convincing, I’d need to take a close look at the code again to determine if a ternary `sign` would have helped it or not, at first glance I think taking the difference, rounding anything “close-to-zero” to zero and using a ternary or binary (not sure which) sign would have helped in that code. Of course this code has `numpy` as a dependency and performance matters, so it would have preferred`np.sign` to `math.sign`. Not the best example in my opinion but sharing it for inspiration.

* * *

All of your suggestions for the expected `sign` function behavior are very helpful thank you.

I definitely agree with all of the suggestions, except for this one which I am (and probably others in this thread are) curious to hear more about:

> [@mdickinson](#):
>
> - raise `ValueError` on NaNs

A lot of the controversy in this thread has been about whether `sign(float("nan"))` should return `nan` or raise an exception (leaving out the suggestion that I think has been agreed to be rejected about it returning `None`). Can you elaborate why you prefer raising an exception to returning a `nan` like `numpy.sign` does?

To be clear I don’t really have much preference for or against either, just trying to collect pros/cons.

* * *

> [@mdickinson](#):
>
> one related possible `math` module enhancement that seems entirely uncontroversial to me would be adding `math.signbit` as a partner to `math.copysign`.

Yeah that seems helpful for use cases where someone really wants to read but not actually copy the sign bit. It would also probably cover most use cases for a `sign` function (e.g. non-zero, non-nan inputs). I’ve been trying to emphasize utility for beginner/naive users and I don’t think `signbit` fully satisfies that. It would be preferable to have some kind of sign detecting function that, frankly, allows users to detect the sign of numbers without needing to hear about what a float is or that it has a sign bit.

> [@skirpichev](#):
>
> It looks like the math module was born as a [libm](https://en.cppreference.com/w/c/numeric/math) wrapper.

I think this is where some of the tension I’m detecting is coming from. I’m wanting a dead-simple function that is a quality-of-life improvement for people who don’t want to care about/want to push under the rug the finer details of floating point arithmetic, but it seems like the `math` module has specifically evolved as a tool (or out of a tool) to help users who do specifically care about the low-level finer points of floating point arithmetic. Just an observation from me. I’d like to hear others’ take on this observation.

* * *

> [@oscarbenjamin](#):
>
> > [@mdickinson](#):
> >
> > raise `TypeError` on `complex` numbers - for all the discussion above about complex inputs, this feels like a YAGNI to me.
> 
> This seems too restrictive especially if there is to be a ` __sign__ ` method which many Python libraries would want to define for complex inputs. I understand why you want this because it allows the value to be an integer always but I would say that `sign` should be defined wherever `abs` is defined and that ` __sign__ ` and ` __abs__ ` can be complementary special methods.

Given the `math` modules explicit documentation that it doesn’t handle complex input it seems like `math.sign()` should not handle complex input. That said, the discussion about ` __sign__ ` and ` __abs__ ` makes me wonder:

## what about `sign()` as a built-in function just like `abs()`?

I would then expect built-in `sign()` function to give `z/|z|` for all input except `0` on which it returns `0`. Following the above suggestion, for int/float/fraction/Decimal `z`, perhaps `sign(z)` would return an int but for complex `z` it would of course return complex. My question is would `float(0j)` return `0j` or `0`?

---

_[View the full topic](https://discuss.python.org/t/include-math-sign/45404)._
