In computer science and numerical analysis, unit in the last place or unit of least precision (ulp) is the spacing between two consecutive floating-point numbers, i.e., the value the least significant digit (rightmost digit) represents if it is 1.<!-- Strictly speaking, when dealing with the representation, it needs to be normalized (e.g. in IEEE 754 decimal arithmetic); but perhaps this would be too much detailed for the LEDE. --> It is used as a measure of accuracy in numeric calculations.
Definition
The most common definition is: In radix <math>b</math> with precision <math>p</math>, if <math>b^e \le |x| < b^{e+1}</math>, then where <math>e_\min</math> is the minimal exponent of the normal numbers. In particular, <math>\operatorname{ulp}(x) = b^{e - p + 1}</math> for normal numbers, and <math>\operatorname{ulp}(x) = b^{e_\min - p + 1}</math> for subnormals.
<!-- TODO: Say something about ulp(0), not defined above. See what the sources (books, programming languages, libraries) say... But note that some documents use a definition for their own purpose, for practical reasons; this should not be regarded as a standard definition. -->
Another definition, suggested by John Harrison, is slightly different: <math>\operatorname{ulp}(x)</math> is the distance between the two closest straddling floating-point numbers <math>a</math> and <math>b</math> (i.e., satisfying <math>a \le x \le b</math> and <math>a \neq b</math>), assuming that the exponent range is not upper-bounded. These definitions differ only at signed powers of the radix.
Since the 2010s, advances in floating-point mathematics have allowed correctly rounded functions to be almost as fast in average as these earlier, less accurate functions. A correctly rounded function would also be fully reproducible. which theoretically would only produce one incorrect rounding out of 1000 random floating-point inputs.
Examples
Example 1
Let <math>x</math> be a positive floating-point number and assume that the active rounding mode is round to nearest, ties to even, denoted <math>\operatorname{RN}</math>. If <math>\operatorname{ulp}(x) \le 1</math>, then <math>\operatorname{RN} (x + 1) > x</math>. Otherwise, <math>\operatorname{RN} (x + 1) = x</math> or <math>\operatorname{RN} (x + 1) = x + \operatorname{ulp}(x)</math>, depending on the value of the least significant digit and the exponent of <math>x</math>. This is demonstrated in the following Haskell code typed at an interactive prompt:
<syntaxhighlight lang="lhaskell">
> until (\x -> x == x+1) (+1) 0 :: Float
1.6777216e7
> it-1
1.6777215e7
> it+1
1.6777216e7
</syntaxhighlight>
Here we start with 0 in single precision (binary32) and repeatedly add 1 until the operation does not change the value. Since the significand for a single-precision number contains 24 bits, the first integer that is not exactly representable is 2<sup>24</sup>+1, and this value rounds to 2<sup>24</sup> in round to nearest, ties to even. Thus the result is equal to 2<sup>24</sup>.
Example 2
The following example in Java approximates Pi| as a floating-point value by finding the two double values bracketing <math>\pi</math>: <math>p_0 < \pi < p_1</math>.
<syntaxhighlight lang="Java">
// π with 20 decimal digits
BigDecimal π = new BigDecimal("3.14159265358979323846");
// truncate to a double floating point
double p0 = π.doubleValue();
// -> 3.141592653589793 (hex: 0x1.921fb54442d18p1)
// p0 is smaller than π, so find next number representable as double
double p1 = Math.nextUp(p0);
// -> 3.1415926535897936 (hex: 0x1.921fb54442d19p1)
</syntaxhighlight>
Then <math>\operatorname{ulp}(\pi)</math> is determined as <math>\operatorname{ulp}(\pi) = p_1 - p_0</math>.
<syntaxhighlight lang="Java">
// ulp(π) is the difference between p1 and p0
BigDecimal ulp = new BigDecimal(p1).subtract(new BigDecimal(p0));
// -> 4.44089209850062616169452667236328125E-16
// (this is precisely 2**(-51))
// same result when using the standard library function
double ulpMath = Math.ulp(p0);
// -> 4.440892098500626E-16 (hex: 0x1.0p-51)
</syntaxhighlight>
Example 3
Another example, in Python, also typed at an interactive prompt, is:
<syntaxhighlight lang="pycon">
>>> x = 1.0
>>> p = 0
>>> while x != x + 1:
... x = x * 2
... p = p + 1
...
>>> x
9007199254740992.0
>>> p
53
>>> x + 2 + 1
9007199254740996.0
</syntaxhighlight>
In this case, we start with <code>x = 1</code> and repeatedly double it until <code>x = x + 1</code>. Similarly to Example 1, the result is 2<sup>53</sup> because the double-precision floating-point format uses a 53-bit significand.
Language support
The Boost C++ libraries provides the functions <code>boost::math::float_next</code>, <code>boost::math::float_prior</code>, <code>boost::math::nextafter</code>
and <code>boost::math::float_advance</code> to obtain nearby (and distant) floating-point values, and <code>boost::math::float_distance(a, b)</code> to calculate the floating-point distance between two doubles.
The C language library provides functions to calculate the next floating-point number in some given direction: <code>nextafterf</code> and <code>nexttowardf</code> for <code>float</code>, <code>nextafter</code> and <code>nexttoward</code> for <code>double</code>, <code>nextafterl</code> and <code>nexttowardl</code> for <code>long double</code>, declared in <code><math.h></code>. It also provides the macros <code>FLT_EPSILON</code>, <code>DBL_EPSILON</code>, <code>LDBL_EPSILON</code>, which represent the positive difference between 1.0 and the next greater representable number in the corresponding type (i.e. the ulp of one).
The Go standard library provides the functions <code>math.Nextafter</code> (for 64 bit floats) and <code>math.Nextafter32</code> (for 32 bit floats) both of which return the next representable floating-point value towards another provided floating-point value.
The Java standard library provides the functions and . They were introduced with Java 1.5.
The Swift standard library provides access to the next floating-point number in some given direction via the instance properties <code>nextDown</code> and <code>nextUp</code>. It also provides the instance property <code>ulp</code> and the type property <code>ulpOfOne</code> (which corresponds to C macros like <code>FLT_EPSILON</code>) for Swift's floating-point types.
See also
- IEEE 754
- ISO/IEC 10967, part 1 requires an ulp function
- Least significant bit (LSB)
- Machine epsilon
- Round-off error
References
Bibliography
- Goldberg, David (1991–03). "Rounding Error" in "What Every Computer Scientist Should Know About Floating-Point Arithmetic". Computing Surveys, ACM, March 1991. Retrieved from http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html#689.
