Math Utilities

Java supports integer and floating-point arithmetic directly in the language. Higher-level math operations are supported through the java.lang.Math class. As you may have seen by now, wrapper classes for primitive data types allow you to treat them as objects. Wrapper classes also hold some methods for basic conversions.

First, a few words about built-in arithmetic in Java. Java handles errors in integer arithmetic by throwing an ArithmeticException:

int zero = 0;

try {
int i = 72 / zero;
} catch ( ArithmeticException e ) {
// division by zero
}

To generate the error in this example, we created the intermediate variable zero. The compiler is somewhat crafty and would have caught us if we had blatantly tried to perform division by a literal zero.

Floating-point arithmetic expressions, on the other hand, don’t throw exceptions. Instead, they take on the special out-of-range values shown in Table 11-1.

Table 11-1. Special floating-point values

Value

Mathematical representation

POSITIVE_INFINITY

1.0/0.0

NEGATIVE_INFINITY

-1.0/0.0

NaN

0.0/0.0

The following example generates an infinite result:

double zero = 0.0;
double d = 1.0/zero;

if ( d == Double.POSITIVE_INFINITY )
System.out.println( "Division by zero" );

The special value NaN (not a number) indicates the result of dividing zero by zero. This value has the special mathematical distinction of not being equal to itself (NaN != NaN evaluates to true). Use Float.isNaN() or Double.isNaN() to test for NaN.

The java.lang.Math Class

The java.lang.Math class is Java’s math library. It holds a suite of static methods covering all of the usual mathematical operations like sin(), cos(), and sqrt(). The Math class isn’t very object-oriented (you can’t create an instance of Math). Instead, it’s really just a convenient holder for static methods that are more like global functions. As we saw in Chapter 6, it’s possible to use the static import functionality to import the names of static methods and constants like this directly into the scope of our class and use them by their simple, unqualified names.

Table 11-2 summarizes the methods in java.lang.Math.

Table 11-2. Methods in java.lang.Math

Method

Argument type(s)

Functionality

Math.abs(a)

int, long, float, double

Absolute value

Math.acos(a)

double

Arc cosine

Math.asin(a)

double

Arc sine

Math.atan(a)

double

Arc tangent

Math.atan2(a,b)

double

Angle part of rectangular-to-polar coordinate transform

Math.ceil(a)

double

Smallest whole number greater than or equal to a

Math.cbrt(a)

double

Cube root of a

Math.cos(a)

double

Cosine

Math.cosh(a)

double

Hyperbolic cosine

Math.exp(a)

double

Math.E to the power a

Math.floor(a)

double

Largest whole number less than or equal to a

Math.hypot(a,b)

double

Precision calculation of the sqrt() of a2 + b2

Math.log(a)

double

Natural logarithm of a

Math.log10(a)

double

Log base 10 of a

Math.max(a, b)

int, long, float, double

The value a or b closer to Long.MAX_VALUE

Math.min(a, b)

int, long, float, double

The value a or b closer to Long.MIN_VALUE

Math.pow(a, b)

double

a to the power b

Math.random()

None

Random-number generator

Math.rint(a)

double

Converts double value to integral value in double format

Math.round(a)

float, double

Rounds to whole number

Math.signum(a)

double, float

Get the sign of the number at 1.0, –1.0, or 0

Math.sin(a)

double

Sine

Math.sinh(a)

double

Hyperbolic sine

Math.sqrt(a)

double

Square root

Math.tan(a)

double

Tangent

Math.tanh(a)

double

Hyperbolic tangent

Math.toDegrees(a)

double

double

log(), pow(), and sqrt() can throw a runtime ArithmeticException. abs(), max(), and min() are overloaded for all the scalar values, int, long, float, or double, and return the corresponding type. Versions of Math.round() accept either float or double and return int or long, respectively. The rest of the methods operate on and return double values:

double irrational = Math.sqrt( 2.0 ); // 1.414...
int bigger = Math.max( 3, 4 );  // 4
long one = Math.round( 1.125798 ); // 1

For convenience, Math also contains the static final double values E and PI:

double circumference = diameter  * Math.PI;

Big/Precise Numbers

If the long and double types are not large or precise enough for you, the java.math package provides two classes, BigInteger and BigDecimal, that support arbitrary-precision numbers. These full-featured classes have a bevy of methods for performing arbitrary-precision math and precisely controlling rounding of remainders. In the following example, we use BigDecimal to add two very large numbers and then create a fraction with a 100-digit result:

long l1 = 9223372036854775807L; // Long.MAX_VALUE
long l2 = 9223372036854775807L;
System.out.println( l1 + l2 ); // -2 ! Not good.

try {
BigDecimal bd1 = new BigDecimal( "9223372036854775807" );
BigDecimal bd2 = new BigDecimal( 9223372036854775807L );
System.out.println( bd1.add( bd2 ) ); // 18446744073709551614

BigDecimal numerator = new BigDecimal(1);
BigDecimal denominator = new BigDecimal(3);
BigDecimal fraction =
numerator.divide( denominator, 100, BigDecimal.ROUND_UP );
// 100 digit fraction = 0.333333 ... 3334
}
catch (NumberFormatException nfe) { }
catch (ArithmeticException ae) { }

If you implement cryptographic or scientific algorithms for fun, BigInteger is crucial. Other than that, you’re not likely to need these classes.

Floating-Point Components

As we mentioned in Chapter 4, Java uses the IEEE 754 standard to represent floating-point numbers (float and double types) internally. Those of you familiar with how floating-point math works will already know that “decimal” numbers are represented in binary in this standard by separating the number into three components: a sign (positive or negative), an exponent representing the magnitude in powers of 2 of the number, and a mantissa using up most of the bits to represent the precise value irrespective of its magnitude. While for most applications the precision of float and double-type floating-point numbers is sufficient enough that we don’t need to worry about running into limitations, there are times when specialized apps may wish to work with the floating-point values more directly.

By definition, floating-point numbers trade off precision and scale. Even the smallest Java floating-point type, float, can represent (literally) astronomical numbers ranging from negative 10–45 to positive 1038. This is accomplished, put in decimal terms, by having the mantissa part of the floating-point value represent a fixed number of “digits” and the exponent tell us where to put the decimal point. As the numbers get larger in magnitude, the “precision” therefore gets shifted to the “left” as more digits appear to the left of the decimal point. What this means is that floating-point numbers can very precisly (with a large number of digits) represent small values like pi, but for bigger numbers (in the billions and trillions) those digits will be taken up with the more signifcant digits. Therefore, the gap between any two consecutive numbers that can be represented by a floating-point value grows larger as the numbers get bigger.

For some applications, knowing the limitations may be important. The java.lang.Math class therefore provides a few methods for interrogating floats and doubles about their precision. The Math.ulp() method retrieves the “unit of least precision” for a given floating-point number, which is the smallest value that bits in the mantissa represent at their current exponent. Another way to say this is that the ulp() is the approximate distance from the floating-point number to the next closest higher or lower floating-point number that can be represented. Adding positive values smaller than half the ULP to a float will not yield a new number. Adding values between half and the full ULP will result in the value plus the ULP. The Math.nextUp() method is a convenience that will take a float and tell you the next number that can be represented by adding the ULP.

float trillionish = (float)1e12; // trillionish ~= 999,999,995,904
float ulp = Math.ulp( f ); // ulp = 65536
float next = Math.nextUp( f ); // next ~= 1000000061440
trillionish += 32767; // trillionish still ~= 999,999,995,904. No change!

Additionally, the java.lang.Math class contains the method getExponent(), which retrieves the exponent part of a floating-point number (and from there one could determine the mantissa by division). It is also possible to get the raw bits of a float or double using their corresponding wrapper class methods floatToIntBits() and doubleToRawLongBits() and pick out the (IEEE standard) bits yourself.

Random Numbers

You can use the java.util.Random class to generate random values. It’s a pseudorandom-number generator that is initialized with a 48-bit seed. Because it’s a pseudorandom algorithm, you’ll get the same series of values every time you use the same seed value. The default constructor uses the current time to produce a seed, but you can specify your own value in the constructor:

long seed = mySeed;
Random rnums = new Random( seed );

After you have a generator, you can ask for one or more random values of various types using the methods listed in Table 11-3.

Table 11-3. Random-number methods

Method

Range

nextBoolean()

true or false

nextInt()

–2147483648 to 2147483647

nextInt(int n )

0 to (n – 1) inclusive

nextLong()

–9223372036854775808 to 9223372036854775807

nextFloat()

0.0 inclusive to 1.0 exclusive

nextDouble()

0.0 inclusive to 1.0 exclusive

nextGaussian()

Gaussian distributed double with mean 0.0 and standard deviation of 1.0

By default, the values are uniformly distributed. You can use the nextGaussian() method to create a Gaussian (bell curve) distribution of double values, with a mean of 0.0 and a standard deviation of 1.0. (Lots of natural phenomena follow a Gaussian distribution rather than a strictly uniform random one.)

The static method Math.random() retrieves a random double value. This method initializes a private random-number generator in the Math class the first time it’s used, using the default Random constructor. Thus, every call to Math.random() corresponds to a call to nextDouble() on that random-number generator.

 The generator uses a linear congruential formula. See The Art of Computer Programming, Volume 2: Semi-numerical Algorithms by Donald Knuth (Addison-Wesley).

Get Learning Java, 4th Edition now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.