10

I have some math (in C++) which seems to be generating some very small, near zero, numbers (I suspect the trig function calls are my real problem), but I'd like to detect these cases so that I can study them in more detail.

I'm currently trying out the following, is it correct?

if ( std::abs(x) < DBL_MIN ) {
     log_debug("detected small num, %Le, %Le", x, y);
}

Second, the nature of the mathematics is trigonometric in nature (aka using a lot of radian/degree conversions and sin/cos/tan calls, etc), what sort of transformations can I do to avoid mathematical errors?

Obviously for multiplications I can use a log transform - what else?

Community
  • 1
  • 1
Petriborg
  • 2,940
  • 3
  • 28
  • 49
  • Can we please see an example of said mathematics, so that we can devise something elegant ? What I have under my mind are things like `cos x - 1` when you can get slowly changing results around zero, and have to select carefully the tolerance. – Alexandre C. Aug 08 '11 at 13:04
  • 1
    As for transformations to avoid underflows, the most direct one is to Taylor expand your expressions and use what comes out for small arguments. For example, instead of `1-cos(x)` use `x*x/2` for `x` smaller than some cutoff. – ev-br Feb 19 '12 at 14:43

4 Answers4

5

Contrary to widespread belief, DBL_MIN is not the smallest positive double value but the smallest positive normalized double value. Typically - for 64-bit ieee754 doubles - it's 2-1022, while the smallest positive double value is 2-1074. Therefore

I'm currently trying out the following, is it correct?

if ( std::abs(x) < DBL_MIN ) {
     log_debug("detected small num, %Le, %Le", x, y);
}

may have an affirmative answer. The condition checks whether x is a denormalized (also called subnormal) number or ±0.0. Without knowing more about your specific situation, I cannot tell if that test is appropriate. Denormalized numbers can be legitimate results of calculations or the consequence of rounding where the correct result would be 0. It is also possible that rounding produces numbers of far greater magnitude than DBL_MIN when the mathematically correct result would be 0, so a much larger threshold could be sensible.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • Thanks for your input on this - the problem is still on going, but I haven't had the time to actually go back and work on it. I'll probably try to post a more intelligent question sometime. – Petriborg Feb 20 '12 at 14:23
2

If x is a double, then one problem with this approach is that you can't distinguish between x being legitimately zero, and x being a positive value smaller than DBL_MIN. So this will work if you know x can never be legitimately zero, and you want to see when underflow occurs.

You could also try catching the SIGFPE signal, which will fire on a POSIX-compliant system any time there's a math error including floating-point underflow. See: http://en.wikipedia.org/wiki/SIGFPE

EDIT: To be clear, DBL_MIN is NOT the largest negative value that a double can hold, it is the smallest positive normalized value that a double can hold. So your approach is fine as long as the value can't be zero.

Another useful constant is DBL_EPSILON which is the smallest double value that can be added to 1.0 without getting 1.0 back. Note that this is a much larger value than DBL_MIN. But it may be useful to you since you're doing trigonometric functions that may tend toward 1 instead of tending toward 0.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
BishopRook
  • 1,240
  • 6
  • 11
2

Since you are using C++, the most idiomatic is to use std::numeric_limits from header <limits>.

For instance:

template <typename T>
bool is_close_to_zero(T x)
{
    return std::abs(x) < std::numeric_limits<T>::epsilon();
}

The actual tolerance to be used heavily depends on your problem. Please complete your question with a concrete use case so that I can enhance my answer.

There is also std::numeric_limits<T>::min() and std::numeric_limits<T>::denorm_min() that may be useful. The first one is the smallest positive non-denormalized value of type T (equal to FLT/DBL/LDBL_MIN from <cfloat>), the second one is the smallest positive value of type T (no <cfloat> equivalent).

[You may find this document useful to read if you aren't at ease with floating point numbers representation.]

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • Possibly later today, after I figure out which part of the eqns are causing the problem. Thanks for the C++ tidbit! – Petriborg Aug 08 '11 at 13:14
  • `DBL_MIN` is the smallest positive **normalized** `double` value, so it would correspond to `min()` and not `denorm_min()`. – Daniel Fischer Feb 17 '12 at 16:59
0

The first if check will actually only be true when your value is zero.

For your second question, you imply lots of conversions. Instead, pick one unit (deg or rad) and do all your computational operations in that unit. Then at the very end do a single conversion to the other value if you need to.

Mark B
  • 95,107
  • 10
  • 109
  • 188