17

I've got a double that prints as 0.000000 and I'm trying to compare it to 0.0f, unsuccessfully. Why is there a difference here? What's the most reliable way to determine if your double is zero?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
nick_name
  • 1,353
  • 3
  • 24
  • 39
  • 5
    Do you want to know if it is _exactly_ zero or if it is sufficiently close to zero that it is 0.000000 to 6 decimal places? In other words, is it important to you if it is non-zero but sufficiently close to zero to print the same as zero? – CB Bailey Aug 10 '11 at 09:40
  • 5
    Please show some code, and explain what you mean by "unsuccessfully". – Greg Hewgill Aug 10 '11 at 09:44

4 Answers4

20

To determine whether it's close enough to zero that it will print as 0.000000 to six decimal places, something like:

fabs(d) < 0.0000005

Dealing with small inaccuracies in floating-point calculations can get quite complicated in general, though.

If you want a better idea what value you've got, try printing with %g instead of %f.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Assuming your compiler support it, you should use DBL_MIN rather than an arbitrary constant. DBL_MIN is (usually?) defined as 2.2250738585072014e-308 which is rather a lot less than 0.0000005 and may help eliminate false positives – Steve Mallam Aug 10 '11 at 09:53
  • 6
    @Steve Mallam: If `DBL_MIN` is the smallest representable floating point number, then what would be the purpose of checking for less than that? – Greg Hewgill Aug 10 '11 at 09:54
  • 2
    @Greg: in fact `DBL_MIN` is the smallest normalized double value, the implementation *may* support denormed values that are smaller. – Steve Jessop Aug 10 '11 at 09:58
  • 2
    @Steve Mallam: yeah, depends how the questioner is printing it and how small a value they think should count as zero for their purposes. I was assuming to six decimal places although I didn't state that, hence my code. `DBL_MIN` is very small, though, not many calculations that have errors at all, are quite that close. Sometimes `DBL_EPSILON`, multiplied by the magnitude of the numbers in in the calculation, is good. With `%g`, even the exact double value `0.0` doesn't print as `0.000000`, I don't think any value does. So I don't know exactly what's being asked, this is my best guess, `%f`. – Steve Jessop Aug 10 '11 at 10:15
  • +1 all round, you're both dead right: DBL_EPSILON is the value I _intended_ to suggest! Apologies - guess I commented in haste... – Steve Mallam Aug 10 '11 at 10:56
4

You can do a range. Like -0.00001 <= x <= 0.00001

theknut
  • 2,533
  • 5
  • 26
  • 41
4

This is fundamental problem with floating point arithmetic on modern computers. They are by nature imprecise, and cannot be reliably compared. For example, the language ML explicitly disallows equality comparison on real types because it was considered too unsafe. See also the excellent (if a bit long and mathematically oriented) paper by David Goldberg on this topic.

Edit: tl;dr: you might be doing it wrong.

James Wilcox
  • 5,307
  • 16
  • 25
1

Also, one often overlooked features of floating point number are the denormalized numbers. That's numbers which have the minimal exponent, yet don't fit in the 0.5-1 range.

Those numbers are lower than FLT_MIN for float, and DBL_MIN for double.

A common mistake with using a threshold is to compare two values, or use FLT_MIN/DBL_MIN as limit.

For example, this would lead unlogical result (if you don't know about denormals):

bool areDifferent(float a, float b) {
    if (a == b) return false;  // Or also: if ((a - b) == FLT_MIN) 
    return true;
}


// What is the output of areDifferent(val, val + FLT_MIN * 0.5f) ?
// true, not false, even if adding half the "minimum value".

Denormals also usually implies a performance loss in computation. Yet, you can not disable them, else such code could still produce a DIVIDE BY ZERO floating point exception (if enabled):

float getInverse(float a, float b) {
    if (a != b)
        return 1.0f / (a-b); // With denormals disabled, a != b can be true, but (a - b) can still be denormals, it'll rounded to 0 and throw the exception
    return FLT_MAX;
}
xryl669
  • 3,376
  • 24
  • 47