• Advertisement
Sign in to follow this  

Sign safer on this way ?

This topic is 906 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

Is it really safer to use :

inline int sign(float a)
{
  return (a > 0.0f) - (a < 0.0f);
}

Than :

inline int sign(float a)
{
  return (a > 0.0f) ? 1 : (a < 0.0f) ? -1 : 0;
}

Thanks

 

Share this post


Link to post
Share on other sites
Advertisement
I'm not sure why you think one or the other would be "safer", but the first snippet is less troublesome for the optimizer. It's slightly less explicit, but I would still use it because it's apparent from the function's name what is happening, and it's not difficult to grok either. It's something that can easily be templatized to work with any type, too: http://stackoverflow.com/a/4609795/572743

They are both working correctly, and insofar they are equally "safe". Integral promotion makes sure that the first snippet works (it is undefined what the value of true is, but it is defined that conversion to integer will yield 1), and the second snippet has an explicit ternary operation attached.

The compiler will most likely convert the first snippet into a single floating point comparison against zero, followed by a conditional move on an integer register.

The second snippet is harder, but will likely (assuming a no-suck optimizer) result in the same. It's much more hassle for the optimizer, though.

Share this post


Link to post
Share on other sites
Define safer..?
The first is equivalent to:
inline int sign(float a)
{
  return (a > 0.0f ? 1 : 0) - (a < 0.0f ? 1 : 0);
}
Venturing off-ttopic, is there a reason to have three results? Often it's simpler to say that zero is positive by convention.
Depending on the platform, it may also be advisable not to mix float and int types:
inline float sign(float a)
{
  return (a >= 0.0f) ? 1.0f : 0.0f;
}

Share this post


Link to post
Share on other sites

Venturing off-ttopic, is there a reason to have three results? Often it's simpler to say that zero is positive by convention.


As someone with a mathematical background I would find that extremely vexing and force me to curse the author for at least twenty minutes. If you call it like the popular function then let it do what the popular function does. If you want something different, have the courtesy to call it differently.

Share this post


Link to post
Share on other sites

Talking about safety, wouldn't it make sense to use an epsilon instead of 0.0f, as is done with most floating point math?

const float EPSILON = 0.00001f; // pass in to the function if this needs to be different throughout the program

inline int sign(float a)
{
  return (a > EPSILON) - (a < EPSILON);
}

Unless you explicitely want 0.00000002 to be positive and not 0 in your whole application.

Edited by Juliean

Share this post


Link to post
Share on other sites

Talking about safety, wouldn't it make sense to use an epsilon instead of 0.0f,

0.00001f is many million trillion trillion times bigger than the smallest positive float - that's a large bracket to round to zero without a specific computer-science or maths reason :D

Different algorithms would likely want different biases (often none at all), so I'd not force such a feature across the board. Let each algorithm decide whether it needs to crush such rounding errors.

As someone with a mathematical background I would find that extremely vexing and force me to curse the author for at least twenty minutes. If you call it like the popular function then let it do what the popular function does. If you want something different, have the courtesy to call it differently.

Hah, as a graphics/engine programmer I just don't want the codegen to be crap. e.g. the sign function in HLSL covers these 3 cases, but should almost never be used as there's usually more optimal algorithms than ones that use that function.
I'll remeber to call my function sign_bit if I ever write that "optimized" version :wink:

Share this post


Link to post
Share on other sites

I'll remeber to call my function sign_bit if I ever write that "optimized" version wink.png


That would be acceptable and would limit my amount of cursing down to the baseline.

Share this post


Link to post
Share on other sites

Talking about safety, wouldn't it make sense to use an epsilon instead of 0.0f, as is done with most floating point math?

const float EPSILON = 0.00001f; // pass in to the function if this needs to be different throughout the program

inline int sign(float a)
{
  return (a > EPSILON) - (a < EPSILON);
}
Unless you explicitely want 0.00000002 to be positive and not 0 in your whole application.


Bad, bad, BAD idea. This kind of thinking was implemented in some of the code I work with and it has proven so problematic that I couldn't help myself and I down-voted your post. This seems to come from a vague understanding of how floating-point numbers work, where some people imagine them as being imprecise or having error, noise or fuzziness. Floating-point numbers have well-defined values and questions like "is this number more than zero" can be answered unambiguously. The chosen epsilon invariably ends up being inadequate in some future situation and it creates all kinds of problems. You don't need epsilons to handle floating-point numbers.

I have a little more sympathy towards Hodgman's point of view. The vast majority of the time, my programs only need to check inequality between two floating-point numbers, in a way where in case of equality either result is acceptable; life is much simpler if you stay away from equality testing.

However, there are enough situations where I want equality to return 0 that I really wouldn't want sign to return something else at 0. The main example that comes to mind is finding the rank of a float x among a list of floats y[]. You can implement this by adding up sign(x-y). Often you will be interested in the rank of one of the elements in y[], and then you expect the sign to return 0 for this rank to behave sensibly.

Share this post


Link to post
Share on other sites
It seems some people may have forgotten -- or perhaps never knew -- that in floating point math zero has a sign.

You can have both +0.0 and -0.0, and it does make a difference in certain floating point operations. It can trigger the difference between positive or negative infinity, which is about as dramatic a difference as floating point can get.

I agree that the sign of a floating point number is a binary result; either a positive sign or a negative sign, so boolean is a better return type... or an enum with two choices if you prefer.

If you have it, use std::signbit() to do it. If you don't have it and can't get a newer compiler, you'll need some implementation-specific work to properly cast the floating point value and test the high order bit, or other implementation-specific operation.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement