| Home | Libraries | People | FAQ | More

### Obtaining the Size of a Unit In the Last Place - ULP

Function `ulp` gives the size of a unit-in-the-last-place for a specified floating-point value.

##### Synopsis

    #include <boost/math/special_functions/ulp.hpp>

    namespace boost{ namespace math{

    template <class FPT>
    FPT ulp(const FPT& x);

    template <class FPT, class Policy>
    FPT ulp(const FPT& x, const Policy&);

    }} // namespaces

##### Description - ulp

Returns one unit in the last place of x.

Corner cases are handled as followes:

- If the argument is a NaN, then raises a domain_error.
- If the argument is an infinity, then raises an overflow_error.
- If the argument is zero then returns the smallest representable value: for example for type `double` this would be either `std::numeric_limits<double>::min()` or `std::numeric_limits<double>::denorm_min()` depending whether denormals are supported (which have the values 2.`2250738585072014e-308` and `4.9406564584124654e-324` respectively).
- If the result is too small to represent, then returns the smallest representable value.
- Always returns a positive value such that `ulp(x) == ulp(-x)`.

Important: The behavior of this function is aligned to that of Java's ulp function, please note however that this function should only ever be used for rough and ready calculations as there are enough corner cases to trap even careful programmers. In particular:

- The function is asymetrical, which is to say, given `u = ulp(x)` if `x > 0` then `x + u` is the next floating-point value, but `x - u` is not necessarily the previous value. Similarly, if `x < 0` then `x - u` is the previous floating-point value, but `x + u` is not necessarily the next value. The corner cases occur at power of 2 boundaries.
- When the argument becomes very small, it may be that there is no floating-point value that represents one ULP. Whether this is the case or not depends not only on whether the hardware may sometimes support denormals (as signalled by `std::numeric_limits<FPT>::has_denorm`), but also whether these are currently enabled at runtime (for example on SSE hardware, the DAZ or FTZ flags will disable denormal support). In this situation, the `ulp` function may return a value that is many orders of magnitude too large.

In light of the issues above, we recomend that:

- To move between adjacent floating-point values always use float_next, float_prior or nextafter (`std::nextafter` is another candidate, but our experience is that this also often breaks depending which optimizations and hardware flags are in effect).
- To move several floating-point values away use float_advance.
- To calculate the edit distance between two floats use Boost.Math float_distance.

There is none the less, one important use case for this function:

If it is known that the true result of some function is xt and the calculated result is xc, then the error measured in ulp is simply `fabs(xt - xc) / ulp(xt)`.