Apache Commons logo Apache Commons Numbers

The Apache Commons Numbers User Guide

Table of contents

Overview

Apache Commons Numbers provides number types and utilities.

The code originated in the commons-math project but was pulled out into a separate project for better maintainability and has since undergone numerous improvements.

The library is divided into modules:

The commons-numbers-bom artifact provides a Bill of Materials (BOM) to aid in dependency management of the modules.

Examples Modules

In addition to the modules above, the Commons Numbers source distribution contains example code demonstrating library functionality and/or providing useful development utilities. These modules are not part of the public API of the library and no guarantees are made concerning backwards compatibility. The example module parent page contains a listing of available modules.

Angle

The commons-numbers-angle module provides utilities related to the concept of angle (angle API).

The Angle class can be used to convert angles between common units. Sub-classes are used to represent the angle units of degrees, radians and turn.

Angle.Deg a = Angle.Turn.of(0.5).toDeg();
Angle.Rad b = a.toRad();
Angle.Turn c = b.toTurn();
// c.getAsDouble() == 0.5

A normalizer can be used to map any value to the natural interval of the angle unit around a provided lower bound, for example [lo, lo + 360) for degrees:

// Range: [-180, 180)
double angle = Angle.Deg.normalizer(-180).applyAsDouble(270);
// angle == -90.0

The normalizer makes use of the Reduce class which can be applied to any interval:

Reduce reduce = new Reduce(0, 24);
double hours1 = reduce.applyAsDouble(173.5);
double hours2 = reduce.applyAsDouble(23.5);
double hours3 = reduce.applyAsDouble(-10);
// hours1 == 5.5
// hours2 == 23.5
// hours3 == 14.0

The Angle class is used extensively in the commons-geometry project.

The CosAngle class computes the cosine of the angle between two vectors using the dot product of two vectors and their magnitudes:

\( \cos\theta = \frac{\mathbf{a}\cdot\mathbf{b}}{ |\mathbf{a}| |\mathbf{b}| } \)

The computation uses extended precision methods to increase the overall accuracy of the result at the cost of a moderate increase in the number of computations.

double[] v1 = {1, 0};
double[] v2 = {0, 1};
double[] v3 = {7, 7};
double cosAngle1 = CosAngle.value(v1, v1);
double cosAngle2 = CosAngle.value(v1, v2);
double cosAngle3 = CosAngle.value(v1, v3);
// cosAngle1 == 1
// cosAngle2 == 0
// cosAngle3 == 0.7071067811865476  (sqrt(2) / 2)
// Math.toDegrees(Math.acos(cosAngle3)) == 45

Arrays

The commons-numbers-arrays module provides array utilities (arrays API).

The SortInPlace class can sort a number of arrays using the order imposed by a specified array:

double[] x = {3, 1, 2};
double[] y = {1, 2, 3};
double[] z = {0, 5, 7};
SortInPlace.ASCENDING.apply(x, y, z);
// x == [1, 2, 3]
// y == [2, 3, 1]
// z == [5, 7, 0]

The MultidimensionalCounter provides a mapping between a unidimensional storage and a multidimensional conceptual structure. Note that successive values for the unidimensional index are used for successive values of the last index in the indices of the multidimensional structure. When the dimension size is reached the values roll-over to the next dimension. The class ensures a 1-to-1 mapping from any index within the size to a multidimensional position.

MultidimensionalCounter c = MultidimensionalCounter.of(100, 50);
int size = c.getSize();
// size == 5000

int index = 233;
int[] indices1 = c.toMulti(index);
int[] indices2 = c.toMulti(index + 1);
// indices1 = [4, 33]  :  4 * 50 + 33 == 233
// indices2 = [4, 34]  :  4 * 50 + 34 == 234

int index1 = c.toUni(4, 33);      // varargs
int index2 = c.toUni(indices1);
// index == index1 == index2

c.toMulti(49)     // [ 0, 49]
c.toMulti(50)     // [ 1,  0]
c.toMulti(4999)   // [99, 49]

Combinatorics

The commons-numbers-combinatorics module provides combinatorics utilities such as factorial and binomial coefficients (combinatorics API).

The binomial coefficient \( \binom{n}{k} \) can be evaluated as a long, double or using the natural logarithm.

long   bc1 = BinomialCoefficient.value(63, 33);
double bc2 = BinomialCoefficientDouble.value(1029, 514);
double bc3 = LogBinomialCoefficient.value(152635712, 125636);
// bc1 == 7219428434016265740
// bc2 ~ 1.429820686498904e308
// bc3 ~ 1017897.199659759

The factorial \( n! \) can be evaluated as a long, double or using the natural logarithm.

long   f1 = Factorial.value(15);
double f2 = Factorial.doubleValue(170);
double f3 = LogFactorial.create().value(Integer.MAX_VALUE);
// f1 == 1307674368000
// f2 == 7.257415615307999e306
// f3 == 4.3996705655378525e10

Note that if the binomial coefficient or factorial cannot be represented the methods will throw an ArithmeticException in-place of a long value or return infinity in-place of a double value. The logarithm methods will always compute a valid result given the bounds imposed by the int arguments.

The LogFactorial class can be created with a cache size. All values up to this size are computed upon creation. This functionality can be used when a range of values may be repeatedly required.

LogFactorial lf = LogFactorial.create().withCache(50);

Values outside the cache are computed using LogGamma.value(n + 1.0) from the Gamma module; this can be used to avoid the object creation of a single-use instance of LogFactorial.

The combinations defined by the binomial coefficient \( \binom{n}{k} \) can be iterated using the Combinations class:

Combinations.of(4, 2).iterator().forEachRemaining(c -> System.out.println(Arrays.toString(c)));

Outputs:

[0, 1]
[0, 2]
[1, 2]
[0, 3]
[1, 3]
[2, 3]

The lexigraphical order is based on the values in the input array in reverse order. This ordering can be imposed on arbitrary sets using the Comparator<int[]> provided by an appropriate Combination:

List<int[]> list = Arrays.asList(new int[][] {
    {3, 4, 5},
    {3, 1, 5},
    {3, 2, 5},
    {4, 2, 4},
});
list.sort(Combinations.of(6, 3).comparator());
list.forEach(c -> System.out.println(Arrays.toString(c)));

Outputs:

[4, 2, 4]
[3, 1, 5]
[3, 2, 5]
[3, 4, 5]

The Stirling class can evaluate Stirling numbers of the first kind and second kind. The Stirling numbers of the first kind \( s(n, k) \) arise in the study of permutations, particularly counting the permutations of a set according to their number of cycles. The Stirling number of the second kind \( S(n, k) \) is the number of ways of partitioning a set of \( n \) elements into \( k \) non-empty subsets. For example a set of 3 elements may be partitioned into:

Stirling.stirlingS2(3, 3)    //  1 : {{1}, {2}, {3}}
Stirling.stirlingS2(3, 2)    //  3 : {{1, 2}, {3}}, {{1, 3}, {2}} and {{1}, {2, 3}}
Stirling.stirlingS2(3, 1)    //  1 : {{1, 2, 3}}

The evaluation is limited by the long datatype and the method will raise an ArithmeticException if the result cannot be represented.

Complex

The commons-numbers-complex module provides utilities for working with complex numbers (complex API).

The Complex class is a Cartesian representation of a complex number. The complex number is expressed in the form \( a + ib \) where \( a \) and \( b \) are real numbers and \( i \) is the imaginary unit which satisfies the equation \( i^2 = -1 \). For the complex number \( a + ib \), \( a \) is called the real part and \( b \) is called the imaginary part. Arithmetic in this class conforms to the C99 standard for complex numbers defined in ISO/IEC 9899, Annex G.

The Complex class is immutable. Instances are constructed with factory methods:

double x = 3;
double y = 4;
Complex c1 = Complex.ofCartesian(x, y);
// c1 == x + iy

// Convert from polar coordinates
double rho = 1.23;
double theta = Math.PI / 2;
Complex c2 = Complex.ofPolar(rho, theta);

Arithmetic operations, elementary functions and trigonometric functions are provided that return new instances of Complex or primitive data types:

Complex c1 = Complex.ofCartesian(3, 4);
Complex c2 = Complex.ofCartesian(5, 6);
Complex c3 = c1.multiply(c2).sqrt();

double magnitude = c3.abs();
double argument = c3.arg();

boolean finite = c3.isFinite();

Core

The commons-numbers-core module provides basic utilities (core API). The classes within this module provide core functionality reused within Apache Commons Numbers and also other projects such as commons-geometry and commons-math.

The interfaces Addition and Multiplication provide generic typed definitions of the arithmetic add and multiply operations. These are extended by NativeOperators that adds operators that can be implemented in a more performant way. These interfaces are used by the Field module.

The ArithmeticUtils class provides arithmetic utilities such as integer power, unsigned divide and remainder functions; greatest common divisor; and least common multiple. These functions are used by the Fraction module.

The Norm class provides implementations of norm functions. The implementations may use extended precision methods to increase the overall accuracy of the result at the cost of a moderate increase in the number of computations. The Euclidean implementation handles possible under and overflow of intermediates using scaling.

double x = Norm.EUCLIDEAN.of(3, -4);                              // 5
double y = Norm.MANHATTAN.of(3, -4, 5);                           // 12
double z = Norm.MAXIMUM.of(new double[]{3, -4, 5, -6, -7, -8});   // 8

double big = Double.MAX_VALUE * 0.5;
double length = Norm.EUCLIDEAN.of(big, big, big);
// length == Math.sqrt(0.5 * 0.5 * 3) * Double.MAX_VALUE

The Sum class provides accurate floating-point sums and linear combinations. The class uses techniques to mitigate round off errors resulting from standard floating-point operations, increasing the overall accuracy of results at the cost of a moderate increase in the number of computations. The Sum can add numbers and products of numbers. The result is returned as the high-precision value if it is finite, or the standard IEEE754 result otherwise.

double sum1 = Sum.create().add(1)
                          .addProduct(3, 4)
                          .getAsDouble();
double sum2 = Sum.of(1).addProduct(3, 4)
                       .getAsDouble();
double sum3 = Sum.ofProducts(new double[] {3, 4}, new double[] {5, 6})
                 .getAsDouble();
// sum1 == sum2 == 13.0
// sum3 == 3 * 5 + 4 * 6

Sum.of(1, 2, Double.NaN).getAsDouble()                 // NaN
Sum.of(1, 2, Double.NEGATIVE_INFINITY).getAsDouble()   // -inf

The implementation provides up to a 106 bit floating point significand. However the significand is split into two double values which may be separated by more than 2^53 by using the exponent of the second double. This provides protection against cancellation in situations that would not be handled by an IEEE754 binary 128 format with a 113 bit significand:

double x1 = 1e100 + 1 - 2 - 1e100;
double x2 = Sum.of(1e100, 1, -2, -1e100).getAsDouble();
// x1 == 0.0
// x2 == -1.0

Note that the first part of the sum is maintained as the IEEE754 result. The Sum is therefore not a full double-double implementation, which would maintain the sum as the current total and a round-off term. This difference makes the Sum class faster at the cost of some accuracy during addition of terms that cancel.

If the terms to be subtracted are known then the summation can be split into the positive and negative terms, summed in two parts and the result computed by a final subtraction of the Sum of negative parts.

double x1 = 1e100 + 1 - 2 - 1e100;
Sum s1 = Sum.of(1e100, 1);
Sum s2 = Sum.of(2, 1e100);
double x2 = s1.subtract(s2).getAsDouble();
// x1 == 0.0
// x2 == -1.0

The class can be used to:

  • Provide accurate summation of numbers with the same sign irrespective of input order;
  • Reduce cancellation effects that occur when large magnitude numbers with opposite signs are summed together with relatively small values;
  • Compute using two sums the terms of the same sign and the final result by combining the sums (using add or subtract).

Precision

The Precision class provides comparison of floating-point numbers using relative, absolute and unit of least precision (ULP) tolerances. Comparison using the relative tolerance \( \epsilon \) is symmetric as the relative delta is computed using the largest magnitude of the input pair of numbers:

\( \frac{|a - b|}{\operatorname{max}(|a|, |b|)} \le \epsilon \)

Comparisons using the ULP argument specifies the allowed distance between two numbers. Numbers are equal if they have (ulp - 1) representable numbers or fewer between a and b. Use ulp = 0 for binary equality.

Note: The default implementation for equality allows no numbers between a and b. It is equivalent to Precision.equals(a, b, 1). Thus using a floating-point delta of 0.0 may indicate numbers are equal when they are not binary equal (see example below).

// Default allows no numbers between
Precision.equals(1000.0, 1000.0)                              // true
Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0))           // true
Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0))       // false

// Absolute - tolerance is floating-point
Precision.equals(1000.0, 1001.0)                              // false
Precision.equals(1000.0, 1001.0, 1.0)                         // true
Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0), 0.0)      // true  (but not binary equal)

// ULP - tolerance is integer
Precision.equals(1000.0, 1001.0)                              // false
Precision.equals(1000.0, 1001.0, 1)                           // false
Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 1)    // false
Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 2)    // true  (n - 1) numbers between
Precision.equals(1000.0, 1000.0 + 3 * Math.ulp(1000.0), 2)    // false

// Relative
Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-6)   // false
Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-3)   // true

Equality using NaN values will be false. To allow two NaN values to be equal requires the use of the method equalsIncludingNaN. Special handling of NaN values allows the default implementation to be optimized for the typical use case comparing finite numbers. Note that infinity values are equal.

Precision.equals(Double.NaN, 1000.0)                                   // false
Precision.equals(Double.NaN, Double.NaN)                               // false
Precision.equalsIncludingNaN(Double.NaN, Double.NaN)                   // true

Precision.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)   // true
Precision.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)   // true
Precision.equals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)   // false

Precision provides a compareTo method to provide ordering of numbers that are not considered equal using the tolerance:

Precision.compareTo(100, 100, 0.0)   // 0
Precision.compareTo(100, 101, 1.0)   // 0
Precision.compareTo(100, 102, 1.0)   // -1
Precision.compareTo(102, 100, 1.0)   // 1

The comparison uses the natural ordering and is symmetric with regard to NaN values. The comparison using <, ==, > is extended in the DoubleEquivalence interface that supports <=, >= and signum.

Precision.DoubleEquivalence eq = Precision.doubleEquivalenceOfEpsilon(1.0);
eq.lt(100, 100)    // false
eq.lte(100, 100)   // true
eq.eq(100, 100)    // true
eq.gte(100, 100)   // true
eq.gt(100, 100)    // false

Precision also provides methods for other floating-point operations such as rounding and computing a representable delta from a number and an ideal delta.

// Round to a number of digits to the right of the decimal point
Precision.round(678.125, 4)              // 678.125
Precision.round(678.125, 3)              // 678.125
Precision.round(678.125, 2)              // 678.13
Precision.round(678.125, 1)              // 678.1
Precision.round(678.125, 0)              // 678.0
Precision.round(678.125, -1)             // 680.0
Precision.round(678.125, -2)             // 700.0

Precision.representableDelta(1.0, 0.1)   // 0.10000000000000009

DD: double-double

The DD class provides an extended precision floating-point number. A double-double is an unevaluated sum of two IEEE double precision numbers capable of representing at least 106 bits of significand. A normalized double-double number \( (x, xx) \) satisfies the condition that the parts are non-overlapping in magnitude such that:

|x| > |xx|
x == x + xx

The implementation assumes a normalized representation during operations on a DD number and computes results as a normalized representation. The number is limited to the same exponent range as a double. Thus the number can be considered as a standard IEEE double with additional information that would be lost to rounding. This information can be used in arithmetic operations so that compound calculation can reduce the error present in the final result returned as a double.

The number \( (x, xx) \) may also be referred to using the labels high and low to indicate the magnitude of the parts as \( ( x_{hi}, x_{lo} ) \), or using a numerical suffix for the parts as \( ( x_0, x_1 ) \). The numerical suffix is typically used when the extended floating-point number has an arbitrary number of parts, for example a quad-double is \( ( x_0, x_1, x_2, x_3 ) \). The parts of the DD can be accessed using the hi() and lo() methods and the evaluated sum \( x + xx \) as doubleValue().

The DD class is immutable. Instances are constructed with factory methods. A DD can be constructed from a double, int or long primitive. In the case of a long this maintains the full 64-bits of information; this is not possible when using implicit type conversion to a double. These conversions can be reversed as the DD provides type conversions using the java.lang.Number methods.

double x = Math.PI;
int    y = 42;
long   z = -8564728970587006436L;
DD.of(x).doubleValue()  // = x
DD.of(y).intValue()     // = y
DD.of(z).longValue()    // = z
                        // (long) (double) z != z

The DD class can be created as the result of a double operation and the round-off error. In the case of addition and multiplication the result is exact. For division the result will have the closest 106-bit representation to the exact result. Note that use of the factory constructors for double arithmetic is preferred over constructing the arguments as a DD from a double and performing the equivalent operation in DD arithmetic:

double a = 1.2345678901234567;
double b = 123.45678901234567;
DD w = DD.ofProduct(a, b);     // (152.41578753238835,-1.0325951435749745E-14)
DD x = DD.ofSum(a, b);         // (124.69135690246912,-1.1102230246251565E-15)
DD y = DD.ofDifference(a, b);  // (-122.22222112222221,-1.1102230246251565E-15)
DD z = DD.fromQuotient(1, 3);  // (0.3333333333333333,1.850371707708594E-17)
// w.hi() = a * b
// x.hi() = a + b
// y.hi() = a - b
// z.hi() = 1.0 / 3

// Inefficient
DD zz = DD.of(1).divide(DD.of(3));
z.equals(zz)   // true

The DD can also be converted from and to a BigDecimal. The conversion from may lose precision. The conversion to is exact but only supported for finite numbers as BigDecimal does not support infinity and NaN. The method isFinite() can be used to verify that the evaluated sum \( x + xx \) is finite; this is only true when both parts are finite and conversion to BigDecimal is possible. Note that BigDecimal can be used for numerical equality and ranking operations as the DD class does not support java.lang.Comparable, and the equals(Object) method is true for numerical equality of the parts, not numerical equality of their evaluated sum. The following demonstrates a round trip of \( \pi \) to 50 decimal places. The DD is limited to approximately 34 decimal places and this is split into the double value for \( \pi \) and a double representing the rounding error.

BigDecimal pi = new BigDecimal("3.14159265358979323846264338327950288419716939937510");
DD x = DD.from(pi);         // (3.141592653589793,1.2246467991473532E-16)
pi.compareTo(x.bigDecimalValue())  // != 0
// x.hi() = Math.PI
// x.lo() = pi.subtract(new BigDecimal(Math.PI)).doubleValue()

DD nan = DD.of(Double.NaN);
nan.isFinite()          // false
nan.bigDecimalValue()   // throws NumberFormatException

Note: It is not possible to directly specify the two parts of the DD number. The two parts must be added using a sum to ensure the DD maintains a normalized representation. If the two parts already represent a number \( (x, xx) \) such that \( x == x + xx \) then the magnitudes of the parts will be unchanged; any signed zeros may be subject to a sign change.

The DD class provides the arithmetic operations add, subtract, multiply, and divide for a double or DD argument. int and long arguments can be used as a double due to implicit type conversion. However if the long value has more than 53-bits of precision then it should first be converted to a DD.

long x = -8564728970587006436L;
DD.ONE.add(x).longValue()         // != x + 1
DD.ONE.add(DD.of(x)).longValue()  // == x + 1

The arithmetic methods are not intended to provide an exactly rounded result to 106-bit precision. A compromise has been made between accuracy and performance so that the DD class is a viable alternative to using BigDecimal for high accuracy arithmetic when the ultimate result is required as a double. The approximate error bounds of operations are supplied in the class javadoc. Typical errors bounds are within a relative error of 1-4 eps of the exact result where eps is \( 2^{-106} \).

The following demonstrates a simple sum where the double representation of a fraction is inexact and results in a rounding error. This is removed by extending the fraction to a double-double representation:

1.0 / 2 + 1.0 / 3 + 1.0 / 6            // = 0.9999999999999999
DD z = DD.fromQuotient(1, 2)
         .add(DD.fromQuotient(1, 3))
         .add(DD.fromQuotient(1, 6));  // (1.0,-4.622231866529366E-33)
z.doubleValue()                        // = 1.0

Note that the error on the final result is influenced by the calculation. For summation of terms of the same sign a very large number of operations would be required to observe a 1 ULP error in the final double result. If terms of the opposite sign are summed then smaller magnitude intermediate terms can be lost due to the limited 106-bit precision. In this case almost total cancellation will produce the incorrect result.

double a = 1;
double b = Math.pow(2, 53);
double c = Math.pow(2, 106);
DD.of(a).add(b).add(c).subtract(c).subtract(b)  // (0, 0)
                                                // NOT (1, 0)

When using products the compound error will accumulate faster but is not influenced by the sign of the operands. A power function pow(int) is provided for compound multiplication of the same argument that is more efficient but does not reduce the error bound.

The DDMath class provides special support for operations using the DD class. The purpose is to supplement the arithmetic operations in the DD class providing greater accuracy at the cost of performance. This includes a power function that returns the result in a fractional representation to avoid over or underflow, with a lower error bound than the equivalent function in DD.

Overflow and underflow

A double-double number is limited to the same finite range as a double. The class is intended for use when the ultimate result is finite and intermediate values do not approach infinity or zero.

This implementation does not support IEEE standards for handling infinite and NaN when used in arithmetic operations. Computations may split a 64-bit double into two parts and/or use subtraction of intermediate terms to compute round-off parts. These operations may generate infinite values due to overflow which then propagate through further operations to NaN, for example computing the round-off using Inf - Inf = NaN.

Operations that involve splitting a double (multiply, divide) are safe when the base 2 exponent is below 996. This puts an upper limit of approximately +/-6.7e299 on any values to be split; in practice the arguments to multiply and divide operations are further constrained by the expected finite value of the product or quotient.

Likewise the smallest value that can be represented is \( 2^{-1074} \). The full 106-bit accuracy will be lost when intermediates are within \( 2^{53} \) of the minimum normalized double \( 2^{-1022} \).

The DD result can be verified by checking it is a finite evaluated sum using isFinite(). Computations expecting to approach over or underflow must use scaling of intermediate terms using frexp(int[]) and scalb(int) and appropriate management of the current base 2 scale. The frexp(int[]) method creates a number with a fractional part \( f \) in the range \( [0.5, 1) \) and an exponent part \( 2^b \) such that \( x = f \times 2^b \). The following example demonstrates scaling and rescaling:

double a = 1.5 * Math.pow(2, 1023);
double b = 4 * Math.pow(2, -1022);
DD x = DD.of(a);
DD y = DD.of(b);

// Values too extreme for DD arithmetic!
x.multiply(y).isFinite()  // false

// Create fractional representation as [0.5, 1) * 2^b
int[] xb = {0};
int[] yb = {0};
x = x.frexp(xb);       // (0.75, 0) * 2^1024
y = y.frexp(yb);       // (0.5, 0)  * 2^-1019

DD z = x.multiply(y);  // (0.375, 0)
// Rescale by 2^5
z.scalb(xb[0] + yb[0]).doubleValue()  // = a * b

This simple example shows the added complexity in avoiding under and overflow. Some common operations have been implemented in the Sum and Norm classes with extended precision floating-point arithmetic. These use appropriate scaling and ensure the correct IEEE result is returned in the event of a non-finite result. Algorithms include summation, sum-of-products and Euclidean norms.

Comparison to Math.fma

The fused multiply-add method Math.fma(double, double, double) added in Java 9 returns the exact product of the first two arguments summed with the third argument and then rounded once to the nearest double. This avoids the two rounding operations that would occur using a * b + c as a regular floating-point expression. The result is returned as a double.

The DD class can represent the exact 106-bit product a * b as \( (x, xx) \). Unlike Math.fma, which uses effectively unlimited precision arithmetic, the addend c can only be used if it overlaps either the upper part \( x \) or the lower part \( xx \) of the number. If too small then it will not change \(x, xx \); if too large then the final result will be \( (c, x) \). However the result will be returned with information that can be used in further computation. Also note that Math.fma will compute the correct IEEE result if the value is non-finite. It will also be protected from over and underflow for the intermediate result, for example:

Math.fma(Double.MAX_VALUE, 2.0, -Double.MAX_VALUE)  // = Double.MAX_VALUE
Math.fma(Double.MIN_VALUE, 0.5,  Double.MIN_VALUE)  // = Double.MIN_VALUE * 1.5

The round-off from a standard floating-point expression x * y can be computed using FMA:

double x = ...
double y = ...
DD z = DD.ofProduct(x, y);
z.hi()  // = x * y;
z.lo()  // = Math.fma(x, y, -x * y)

This optimisation is not available on Java 8. The DD class uses standard double arithmetic to split the arguments into parts \( x = x_h + x_l \) and \( y = y_h + y_l \), each of which can be multiplied exactly by double arithmetic and used to compute the round-off component. Splitting and computation of the round-off takes 16 FLOPS which is far slower than a hardware supported FMA operation.

Comparison to IEEE 754 quadruple

A DD class has a 106-bit significand, 11-bit exponent and a sign bit. It is not an equivalent of the IEEE 754 quadruple-precision floating-point format. This has a 113-bit significand, 15-bit exponent and a sign bit. This has both a larger significand and exponent range, and supports arithmetic to the correctly rounded result. The DD class is a software implementation of an extended precision floating-point number with performance compromises made on the supported finite range and precision of operations. The DD is therefore simpler to consider as a IEEE 754 double with an additional +/- 0.5 ULP used to improve accuracy during compound operations.

Application of DD

DD arithmetic is slower than double arithmetic and faster than BigDecimal. The class has been implemented to avoid costly branch conditions in operations, such as those required to support IEEE arithmetic for non-finite values, and avoid significant extra computation for a gain of only 1 or 2 ULP accuracy in the lower part of the double-double number. Users should assess the benefits of using standard double arithmetic, with or without Math.fma, against DD or BigDecimal arithmetic with suitable accuracy and performance benchmark data. For example the DD class is used in Commons Numbers and commons-statistics in targeted parts of function evaluations where accuracy is improved by 8-10 bits over standard double arithmetic, or even to enable computations that have catastrophic error using double arithmetic.

Field

The commons-numbers-field module provides utilities related to the concept of field (field API).

A field is any set of elements that satisfies the field axioms for both addition and multiplication and is a commutative division algebra. The Field interface defines the operations of a field. Implementations are provided for rational numbers as FractionField and BigFractionField; and real numbers as FP64Field.

Field<FP64> field = FP64Field.get();
FP64 result = field.one().multiply(3).divide(field.one().multiply(4));
double value = result.doubleValue();
// value = 0.75

The field abstraction can be used to program computations independently of the underlying representation as any Field<T> can be manipulated using the arithmetic operations of add, subtract, multiply and divide.

Fraction

The commons-numbers-fraction module provides fraction utilities (fraction API). Classes are provided to represent rational numbers and to evaluate continued fractions.

Rational Numbers

The Fraction class is a representation of a rational number p / q using int values for the numerator p and denominator q.

Instances are constructed with factory methods:

int p = 3;
int q = 4;
Fraction f = Fraction.of(p, q);
// f == 3 / 4

Fraction can be created using a double. The floating-point number may not be exactly representable; a continued fraction is used to converge on a rational representation within a specified absolute tolerance, or a maximum denominator. An ArithmeticException is thrown if the algorithm fails to converge or the number is not representable.

int maxIterations = 100;
//                               tolerance
Fraction a = Fraction.from(0.6152, 0.02,     maxIterations);
Fraction b = Fraction.from(0.6152, 1.0e-7,   maxIterations);
// a = 3 / 5      == 0.6
// b = 769 / 1250 == 0.6152 (exact)

//                               max denominator
Fraction c = Fraction.from(0.6152, 9);
Fraction d = Fraction.from(0.6152, 9999);
// c = 3 / 5      == 0.6
// d = 769 / 1250 == 0.6152 (exact)

Fraction e = Fraction.from(Math.pow(2, 31));
// e = Integer.MIN_VALUE / -1

// *** Throws an ArithmeticException ***
Fraction.from(Math.pow(2, 32));

The fraction is represented in reduced form using the greatest common divisor. For example 240 / 256 is reduced to 15 / 16. The sign can be either in the numerator or the denominator; the sign of the fraction is accessed using the signum() method. Fraction equality can be evaluated using equals(). The class implements Comparable<Fraction> to allow sorting using the natural order imposed by the fraction's sign and magnitude.

Fraction f1 = Fraction.of(-240, 256);
Fraction f2 = Fraction.of(15, -16);

f1.signum()                 == -1
f1.equals(f2)               == true
f1.compareTo(f2)            == 0
f1.compareTo(Fraction.ZERO) == -1

Fraction.of(Integer.MIN_VALUE, Integer.MIN_VALUE).compareTo(Fraction.ONE) == 0

Arithmetic operations are provided that return new instances of Fraction. Note that fraction operations are exact.

Fraction result = Fraction.of(1, 2).add(Fraction.of(1, 3))
                                   .add(Fraction.of(1, 6));
// result == 1 / 1
// Note: 1.0 / 2 + 1.0 / 3 + 1.0 / 6 == 0.9999999999999999 (inexact)

Fraction extends java.lang.Number and can be converted to other primitive numbers with possible loss of precision:

double a = Fraction.of(1, 8).doubleValue();
double b = Fraction.of(1, 3).doubleValue();
int    c = Fraction.of(8, 3).intValue();
// a == 0.125     (exact)
// b == 1.0 / 3   (inexact)
// c == 2         (inexact, whole number part of 2 2/3)

Fraction is limited to rational numbers representable using int values. Operations that cannot be represented will raise an ArithmeticException. Intermediate computations may use the long datatype and arithmetic is relatively fast.

The BigFraction class uses BigInteger for the numerator and denominator and can provide exact arithmetic up to the limitations of BigInteger. This is documented as at least +/- 2^Integer.MAX_VALUE (exclusive) and may support values outside of that range.

The BigFraction class provides all the arithmetic operations of Fraction and can be used when Fraction cannot represent the rational numbers; there will be a performance cost to using BigFraction due to the switch from primitive values to BigInteger for computations.

BigFraction can be constructed from int, long or BigInteger numerator and denominators.

BigFraction a = BigFraction.of(1, 3);
// a == 1 / 3

The BigFraction conversion of double values can be exact, or adjusted to be within a provided tolerance. Note that exact conversion may create an unexpected rational number as it is limited by the precision of the input double value which may be inexact:

BigFraction a = BigFraction.from(1.0 / 3);
// a == 6004799503160661 / 18014398509481984

//                                        max denominator
BigFraction b = BigFraction.from(1.0 / 3, 3);
// b == 1 / 3

Continued Fractions

A generalized continued fraction is an expression of the form:

\( x = b_0 + \cfrac{a_1}{b_1 + \cfrac{a_2}{b_2 + \cfrac{a_3}{b_3 + \cfrac{a_4}{b_4 + \ddots\,}}}} \)

Classes are provided to evaluate the expression through iteration to a provided error tolerance. The user must supply the coefficients \( a \) and \( b \).

The ContinuedFraction class requires a function to supply the nth coefficients; the API allows the fraction function to be evaluated for different arguments. This is useful when the coefficients can be computed using a formula based on the index and the argument. The class is abstract and must be extended to provide the coefficients \( a \) and \( b \). For example the following fraction:

\( \tan(z) = \cfrac{z}{1 - \cfrac{z^2}{3 - \cfrac{z^2}{5 - \ddots\,}}} \)

Can be computed using:

ContinuedFraction cf = new ContinuedFraction() {
    @Override
    public double getA(int n, double x) {
        return n < 2 ? x : -x * x;
    }

    @Override
    public double getB(int n, double x) {
        return n == 0 ? 0 : 2 * n - 1;
    }
};

double eps = 1e-8;
double z = 0.125;
double result = cf.evaluate(z, eps);
// result ~ Math.tan(z)

The GeneralizedContinuedFraction class requires a generator for the next pair of coefficients. This is useful when the coefficients can be computed using the previous values. The class evaluates the fraction using static methods and a supplied generator of coefficients. This can be done with the generator supplying the initial term \( b_0 \) where \( a_0 \) is ignored, or with the generator starting at \( (a_1, b_1) \) and the term \( b_0 \) provided separately.

For example the golden ratio:

\( x = 1 + \cfrac{1}{1 + \cfrac{1}{1 + \cfrac{1}{1 + \ddots\,}}} \)

Can be computed as:

double eps = 1e-10;
double gr1 = GeneralizedContinuedFraction.value(() -> Coefficient.of(1, 1), eps);
double gr2 = GeneralizedContinuedFraction.value(1, () -> Coefficient.of(1, 1), eps);
// gr1 ~ gr2 ~ 1.6180339887498948...

Whether to separate the term \( b_0 \) depends on the fraction and the algorithm convergence. See the GeneralizedContinuedFraction documentation for details. Note that it is also possible to evaluate part of the fraction using only later terms and then computing the final result by including the early terms.

An example using a recursive generator for the evaluation of \( \tan(z) \) is computed using:

static class Tan implements Supplier<Coefficient> {
    private double a;
    private double b = -1;

    /**
     * @param z Argument z
     */
    Tan(double z) {
        a = z;
    }

    @Override
    public Coefficient get() {
        b += 2;
        // Special first case
        if (b == 1) {
            double z = a;
            // Update the term for the rest of the fraction.
            // The continuant is subtracted from the b terms, thus all the
            // remaining a terms are negative.
            a *= -z;
            return Coefficient.of(z, b);
        }
        return Coefficient.of(a, b);
    }
}

double z = 0.125;
double tan1 = GeneralizedContinuedFraction.value(0, new Tan(z));

// Advance 1 term
Tan t = new Tan(z);
Coefficient c = t.get();
double tan2 = c.getA() / GeneralizedContinuedFraction.value(c.getB(), t);

// tan1 ~  Math.tan(z)
// tan2 == Math.tan(z)  (more accurate)

A simple continued fraction will have an \( a \) coefficient of 1. For example the following fraction:

\( \frac{415}{93} = 4 + \cfrac{1}{2 + \cfrac{1}{6 + \cfrac{1}{7}}} \)

can be evaluated as a simple continued fraction using:

private static Supplier<Coefficient> simpleContinuedFraction(double... coefficients) {
    return new Supplier<Coefficient>() {
        /* iteration. */
        private int n;
        /* denominator terms. */
        private double[] b = coefficients.clone();

        @Override
        public Coefficient get() {
            if (n != b.length) {
                // Return the next term
                return Coefficient.of(1, b[n++]);
            }
            return Coefficient.of(0, 1);
        }
    };
}

double result1 = GeneralizedContinuedFraction.value(simpleContinuedFraction(4, 2, 6, 7));
double result2 = GeneralizedContinuedFraction.value(4, simpleContinuedFraction(2, 6, 7));
// result1 ~ result2 ~ 415.0 / 93.0

Gamma

The commons-numbers-gamma module provides utilities related to the "Gamma" function (gamma API).

Gamma function \( \Gamma(a) \)
Incomplete gamma functions (lower and upper) \( \Gamma(a) = \gamma(a, x) + \Gamma(a, x) \)
Regularized gamma functions (lower and upper) \( 1 = P(a, x) + Q(a, x) \)
Ratio of gamma functions \( \frac {\Gamma(a) } {\Gamma(b) } \)
Digamma function \( \frac {d } {dx }(\ln \Gamma(x)) \)
Trigamma function \( \psi_1(x) = \frac {d^2 } {dx^2 } (\ln \Gamma(x)) \)
Logarithm of the absolute value of the gamma function \( \ln\, \lvert \Gamma(x) \rvert \)
Beta function \( B(a, b) = \frac {\Gamma(a)\ \Gamma(b) } {\Gamma(a+b) } \)
Incomplete beta function (and the complement) \( B_x(a, b) = \int_0^x t^ {a-1 }\,(1-t)^ {b-1 }\,dt \)
Logarithm of the beta function \( \ln B(a, b) \)
Error function \( \operatorname {erf }(z) \)
Complementary error function \( \operatorname {erfc }(z) \)
Scaled complementary error function \( \operatorname {erfcx }(z) \)
Difference between error function values \( \operatorname {erf }(a) - \operatorname {erf }(b) \)
Inverse error functions

Functions are provided using static class methods. These may be optionally parameterized if the method uses an iterative computation to control the function evaluation. For example:

double result = Erfc.value(1.23);

// Default function evaluation
double a = 1.23;
double x = 4.56;
double result1 = IncompleteGamma.Lower.value(a, x);

// Parameterize function evaluation
double epsilon = 1e-10;
int maxIterations = 1000;
double result2 = IncompleteGamma.Lower.value(a, x, epsilon, maxIterations);
// result1 ~ result2

The gamma functions are used to evaluate many of the probability distributions provided in the commons-statistics project.

Primes

The commons-numbers-primes module provides utilities related to prime numbers (primes API).

The Primes class provides static methods for working with prime numbers in the range of an int. Methods are provided to determine if a value is prime, compute the next prime after a value and the prime factors of a number.

int n = 51237173;
boolean prime = Primes.isPrime(n);
int     m     = Primes.nextPrime(n);
// prime == false
// m     == 51237233

List<Integer> f1 = Primes.primeFactors(n);
List<Integer> f2 = Primes.primeFactors(m);
// f1 == [13, 863, 4567]
// f2 == [51237233]
// n  == 13 * 863 * 4567

Quaternion

The commons-numbers-quaternion module provides utilities for working with quarternion numbers (quaternion API).

The Quaternion class represents Hamilton's hypecomplex number in the form \( H = w + x\,i + y\,j + z\,k \), where \( w \) is the scalar component and \( [x, y, z] \) is the vector component.

The class is immutable and instances are constructed with factory methods. Getters are provided for access to the components:

double w = 2;
double x = 3;
double y = 4;
double z = 5;
Quaternion h1 = Quaternion.of(w, x, y, z);
Quaternion h2 = Quaternion.of(w, new double[]{x, y, z});
h1.getW();            // w
h1.getScalarPart();   // w
h1.getVectorPart();   // [x, y, z]
// h1 == h2

Methods are provided for arithmetic (add, subtract, multiply) and other quaternion operations such as scaling, dot product, negation, conjugation and normalization.

Quaternion h = Quaternion.of(1, 2, 3, 4);
Quaternion h1 = h.add(Quaternion.ONE);   // [2, 2, 3, 4]
Quaternion hi = h.add(Quaternion.I);     // [1, 3, 3, 4]
Quaternion hj = h.add(Quaternion.J);     // [1, 2, 4, 4]
Quaternion hk = h.add(Quaternion.K);     // [1, 2, 3, 5]

The binary operations acting on two Quaternion instances are provided as instance methods and static methods for convenience when using method references as lambda functions:

Quaternion h1 = Quaternion.of(1, 2, 3, 4);
Quaternion h2 = Quaternion.of(5, 6, 7, 8);
Quaternion result1 = h1.multiply(h2);
Quaternion result2 = Quaternion.multiply(h1, h2);
// result1 == result2

The class evaluates binary equality using equals(Object) and provides a helper method to evaluate equality within an absolute tolerance:

Quaternion q1 = Quaternion.of(1, 2, 3, 4);
Quaternion q2 = Quaternion.of(1, 2, 3, 4 + 1e-10);
q1.equals(q2);         // false
q1.equals(q2, 1e-9);   // true

Quaternions are used to apply rotations in 3-dimensional Euclidean space in the commons-geometry project.

The Slerp class performs spherical linear interpolation (Slerp). The algorithm is designed to interpolate smoothly between two rotations/orientations, producing a constant-speed motion along an arc.

static Quaternion createZRotation(final double theta) {
    double halfAngle = theta * 0.5;
    return Quaternion.of(Math.cos(halfAngle), 0, 0, Math.sin(halfAngle));
}

Quaternion q1 = createZRotation(0.75 * Math.PI);
Quaternion q2 = createZRotation(0.76 * Math.PI);

Slerp slerp = new Slerp(q1, q2);

slerp.apply(0.0)    // ~ q1
slerp.apply(0.25)   // ~ createZRotation(0.7525 * Math.PI)
slerp.apply(0.5)    // ~ createZRotation(0.755  * Math.PI)
slerp.apply(0.75)   // ~ createZRotation(0.7575 * Math.PI)
slerp.apply(1.0)    // ~ q2

Root Finder

The commons-numbers-rootfinder module provides utilities for finding the zero of a function (rootfinder API).

The BrentSolver class implements the Brent algorithm for finding the zeros of real univariate functions. The solver requires an interval that brackets a root and will raise an exception if there is no sign change between the bounds \( [a, b] \). The relative \( \epsilon \) and absolute \( \delta \) accuracy specify the convergence tolerance for the function value \( x \) to zero as: \( 2\,\epsilon\,|x| + \delta \). The function accuracy is used to return the initial bracket bounds, or initial guess, if the function value is within the specified accuracy of zero.

double relAccuracy = 1e-6;
double absAccuracy = 1e-14;
double functionAccuracy = 1e-15;
BrentSolver solver = new BrentSolver(relAccuracy,
                                     absAccuracy,
                                     functionAccuracy);
double result1 = solver.findRoot(Math::sin, 3, 4);
double result2 = solver.findRoot(Math::sin, 3, 3.14, 4);   // With initial guess
// result1 ~ result2 ~ Math.PI

// *** Throws an IllegalArgumentException ***
solver.findRoot(Math::sin, 2, 3);

Dependencies

Apache Commons Numbers requires JDK 1.8+ and has no runtime dependencies.