Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a standard way to compare float with zero?

May question is: What is a standard way to compare float with zero?

As far as I know direct comparison:

if ( x == 0 ) { 
  // x is zero?
} else {
  // x is not zero??

can fail with floating points variables.

I used to use

float x = ...
...
if ( std::abs(x) <= 1e-7f ) { 
  // x is zero, do the job1
} else {
 // x is not zero, do the job2
...

Same approach I find here. But I see two problems:

  1. Random magic number 1e-7f ( or 0.00005 at the link above ).
  2. The code harder to read

This is such a common comparison, I wonder whether there is a standard short way to do this. Like

 x.is_zero();
like image 547
klm123 Avatar asked Nov 03 '13 16:11

klm123


3 Answers

To compare a floating-point value with 0, just compare it:

if (f == 0)
    // whatever

There is nothing wrong with this comparison. If it doesn't do what you expect it's because the value of f is not what you thought it was. Its essentially the same problem as this:

int i = 1/3;
i *= 3;
if (i == 1)
    // whatever

There's nothing wrong with that comparison, but the value of i is not 1. Almost all programmers understand the loss of precision with integer values; many don't understand it with floating-point values.

Using "nearly equal" instead of == is an advanced technique; it often leads to unexpected problems. For example, it is not transitive; that is, a nearly equals b and b nearly equals c does not mean that a nearly equals c.

like image 182
Pete Becker Avatar answered Oct 04 '22 22:10

Pete Becker


There is no standard way, because whether or not you want to treat a small number as if it were zero depends on how you computed the number and what it's for. This in turn depends on the expected size of any errors introduced by your computations, and perhaps on errors of physical measurement that determined your original inputs.

For example, suppose that your value represents the length of a journey in miles in some mapping software. Then you are happy to treat 1e-7 as equal to zero because in that context it is a very small number: it has come about because of a rounding error or other reason for slight inexactness.

On the other hand, suppose that your value represents the size of a molecule in metres in some electron microscopy software. Then you certainly don't want to treat 1e-7 as equal to zero because in that context it's a very large number.

You should first consider what would be a suitable accuracy to present your value: what's the error bar, or how many significant figures can you reasonably display. This will give you some idea with what tolerance it would be appropriate to test against zero, although it still might not settle the case. For the mapping software, you can probably treat a journey as zero if it's less than some fixed value, although the value itself might depend on the resolution of your maps. For the microscopy software, if the difference between two sizes is such that zero lies with the 95% error range on those measurements, that still might not be sufficient to describe them as being the same size.

like image 43
Steve Jessop Avatar answered Oct 04 '22 20:10

Steve Jessop


I don't know whether my answer useful, I've found this in irrlicht's irrmath.h and still using it in engine's mathlib till nowdays:

const float ROUNDING_ERROR_f32 = 0.000001f;

//! returns if a equals b, taking possible rounding errors into account
inline bool equals(const float a, const float b, const float tolerance = ROUNDING_ERROR_f32)
{
        return (a + tolerance >= b) && (a - tolerance <= b);
}

The author has explained this approach by "after many rotations, which are trigonometric operations the coordinate spoils and the direct comparsion may cause fault".

like image 35
Netherwire Avatar answered Oct 04 '22 22:10

Netherwire