Viliam Búr

2008/08/05

Real Numbers in Java

In programming language Java there are two primitive types designed for work with real numbers: float and double. Their sizes are 4 or 8 bytes, which contain 1 sign bit, 8 or 11 exponent bits, and 23 or 52 mantissa bits. The real numbers are rounded after certain number of valid digits; that means that greater numbers have greater rounding errors. We need to make a difference between the ranges of these types (smallest and largest values) and their precisions (number of valid digits).

Each of these primitive types corresponds to an object type: java.lang.Float and java.lang.Double. Their static variables "MIN_VALUE" and "MAX_VALUE" are the smallest and the largest finite positive real numbers which can be expressed using this type.

System.out.println("Float " + Float.MIN_VALUE + " " + Float.MAX_VALUE);
// Float 1.4E-45 3.4028235E38
System.out.println("Double " + Double.MIN_VALUE + " " + Double.MAX_VALUE);
// Double 4.9E-324 1.7976931348623157E308

The formula "1.4E-45" means "1.4 × 10-45", which is 0.000 000 000 000 000 000 000 000 000 000 000 000 000 000 001 4. The formula "3.4028235E38" means "3.4028235 × 1038", which is 340 282 350 000 000 000 000 000 000 000 000 000 000; the trailing zeroes only meaning that the digits on these places have been rounded. Also the formulas "4.9E-324" and "1.7976931348623157E308" mean "4.9 × 10-324" and "1.7976931348623157 × 10308", which I will not expand, because the resulting numbers are over 300 digits long.

What happens when the result of computing is a too large or too small number, outside the limits of this type? Both real types have helper constants, which allow to return at least some results even beyond the type boundaries. Too large positive and negative numbers can be expressed by constants "POSITIVE_INFINITY" and "NEGATIVE_INFINITY". Numbers too close to zero are rounded to zero (there is also a negative zero "-0.0f", which is usually equal to normal zero, except some specific situations like division by zero). If we cannot say anything about the result, such as an infinity minus infinity or zero divided by zero, this can be expressed by a constant "NaN". (Unlike the object constant "null", these constants belong to the types "float" and "double", so they can be stored in primitive type variables too.)

If we want to find out if our calculation has returned one of these values, we can use methods "isInfinite" and "isNaN".

Why do we use these special constants for real numbers, when integer numbers in similar circumstances just overflow or throw an "ArithmeticException"? One reason is that the real numbers are rounded. If we divide by zero in an integer calculation, it usually means that the programmer has forgot to check some special case; and it is good if the program gives him/her a warning. If we divide by zero in a real calculation, it often means that we wanted to divide by some nonzero value, which was rounded to zero during the calculation. Checking for all such situations would be difficult, and essentially useless. A good programmer already knows that the real numbers are rounded, so the results of calculations are always unreliable to some degree; how much exactly can the rounding error grow depends on the specific formula. Another reason is the the way the real numbers are stored in memory allows to add these constants "naturally"; using a similar method for integer numbers would be "unnatural", and by the way it would make all programs much slower.

System.out.println(Float.MAX_VALUE * 2);  // Infinity
System.out.println(Float.MIN_VALUE / 2);  // 0.0
System.out.println(0.0f /  0.0f);  // NaN
System.out.println(1.0f /  0.0f);  // Infinity
System.out.println(1.0f / -0.0f);  // -Infinity
System.out.println(1.0f / Float.POSITIVE_INFINITY);  //  0.0
System.out.println(1.0f / Float.NEGATIVE_INFINITY);  // -0.0

(C) 2008 Viliam Búr viliambur.blogspot.com

Basic mathematical operations are done using symbols: "+" addition, "-" subtraction, "*" multiplication, "/" division. If we use both integer and real numbers, the "/" symbol between two integer numbers means integer division, between two real numbers or between a real and integer numbers it means real division.

System.out.println(1    / 3   );  // 0
System.out.println(1    / 3.0f);  // 0.333...
System.out.println(1.0f / 3   );  // 0.333...
System.out.println(1.0f / 3.0f);  // 0.333...

We can compare numbers using operators: "==" is equal, "!=" is not equal, "<" is less, ">" is more, "<=" is less or equal, ">=" is more or equal. During calculations the real numbers are rounded, so the result of "==" operator may depend on rounding errors. With real numbers we usually do not check whether they are equal, but whether their difference is in some given tolerance.

float a = 123456789f;
float b = 123000123f;
float c = 987987987f;
float x = a * b * c;
float y = c * b * a;
System.out.println(x);  // 1.5002796E25
System.out.println(y);  // 1.5002795E25

Class java.lang.Math provides many methods for calculations with integer and real numbers: absolute value "abs" and sign "signum"; smaller or greater of two numbers "min" and "max"; rounding "floor", "ceil", "round"; power "exp" and "pow", root "sqrt" and "cbrt", and logarithm "log" and "log10"; changing angles from degrees to radians "toRadians" and vice versa "toDegrees"; goniometric functions "sin", "cos", "tan", "asin", "acos", "atan", "atan2" and hyperbolic functions "sinh", "cosh", "tanh". It provides also mathematical constants "E" and "PI".

There is also a class java.lang.StrictMath, which provides the same methods, with additional guarantee that these methods will give exactly the same results when used on any platform.

We can receive random numbers using object java.util.Random. Methods "nextFloat" and "nextDouble" return random real numbers distributed uniformly between 0 and 1. Method "nextGaussian" returns random real numbers according to Gaussian normal distribution with mean 0 and standard deviation 1; this means that approximately 68% of values will not be further from the mean (in any direction) than the standard deviation, 95% of values will not be further than two standard deviations, and 99.7% of values will not be further than three standard deviations. For example the IQ tests have mean 100 and standard deviation 15, so here is a simulator of IQ tests of randomly selected individuals in population:

java.util.Random r = new java.util.Random();
for (int i = 0; i < 100; i++) {
  System.out.println(Math.round(100 + 15 * r.nextGaussian()));
}

Related articles:

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home