Include `math.sign`

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) 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 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 preferrednp.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:

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.


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.

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.


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?