RR: Small numerical values in C++

In numerical programs, financial codes included, there is often a requirement to use very small numerical values in the algorithm, perhaps for ensuring all operations are in a valid domain. In a code published as an accompaniment to a book there is a line:

const double eps=1.e-50;

with the intention to use it later as a small numerical value. Is this a good idea? Can it be done better?

Printing floating point numbers in hexidecimal format

First lets see how 1.e-50 is represented as binary floating point:

// Bojan Nikolic <bojan@bnikolic.co.uk>
//
// In one published code, there is a line:
// const double eps=1.e-50;
// A good idea? How to do better?
#include <cmath>
#include <limits>
#include <iostream>
#include <boost/format.hpp>

void printVal(const std::string &pref,
          double v)
{
  union {
    double d;
    int    b[2];
  } t;
  t.d=v;

  std::cout<<pref
       <<boost::format("\t\t%g is %0#x:%0#x") % t.d % t.b[0] % t.b[1]
       <<std::endl;
}

#define PRINTV(exp) (printVal(#exp, exp))

int main(void)
{
  PRINTV(1.e-50);
}

The output of this program is:

1.e-50              1e-50 is 0x4ad4b81f:0x358dee7a

Clearly nothing much special about the value 1.e-50 in double-precision floating point representation. There is lots more on the representation of floating point numbers at the Wikipedia.

Small numbers and other numeric Limits in C++

The C++ standard defines two useful small numbers for the floating point types, through the template class numeric_limits in the header <limits> (see also documentation on IBM pages):

  • std::numeric_limits::min() : This function returns the positive normalised value closest to zero

    This is the value to use to, for example, avoid zero values in computation: e.g., log(std::numeric_limits<double>::min()+x) if x could be zero

  • std::numeric_limits::epsilon(): This function returns the difference between one and the smallest value greater than one that is representable

    This is the value if a small perturbation to a number is necessary, e.g., f(x+std::numeric_limits<double>::epsilon())-f(x)

For double precision floating point representation, these two numbers are as follows:

std::numeric_limits<double>::min()          2.22507e-308 is 0:0x100000
std::numeric_limits<double>::epsilon()      2.22045e-16 is 0:0x3cb00000

So 1.0e-50 fall very roughly in the middle of the logarithmic range defined by the min() and epsilon() functions for double precision numbers.

Summary

If you need small numerical values for you algorithm, use the quantities returned by numeric_limits::min() and numeric_limits::epsilon(). Both of these are defined in the <limits> standard header.

The two quantities define:

  • The range of the numerical type [min()]
  • And, its precision [epsilon()]

Before you use either, you need to spend a few seconds thinking which one you actually require.