Flylib.com

Books Software

 
 
 

3.6 No Exceptions

   

 
Java Number Cruncher: The Java Programmer's Guide to Numerical Computing
By Ronald  Mak

Table of Contents
Chapter  3.   The Floating-Point Standard

3.6 No Exceptions!

What happens when "bad" things occur during floating-point operations? The IEEE 754 standard specifies standard responses to five kinds of exceptions, as shown in Table 3-3.

Java provides the standard responses. The result of an overflow is either - Infinity or + Infinity, and the result of an underflow is either a denormalized number, -0.0, or +0.0.

The IEEE 754 standard also specifies that, when an exception occurs, the computer must signal it by setting an associated status flag, and this flag must stay set until the program clears it. The standard also specifies that a program be able to trap the exception or ignore it by masking it.

Table 3-3. Standard responses to floating-point exceptions, as specified by the IEEE 754 standard.

Exception

Standard Response

Invalid operation

Set the result to Not-a-Number.

Division by zero

Set the result to ± .

Overflow

Set the result to the largest possible normalized number or to ± .

Underflow

Set the result to ±0, the smallest possible normalized number, or a denormalized number.

Inexact value

Set the result to the correctly rounded value.

Java deviates from this standard. Java's floating-point operations never throw exceptions. They are nonstop. Programs must check for the exceptions indirectly, such as by testing for NaN using the Float.isNaN() and Double.isNaN() methods , and we must be especially wary when results become - Infinity or + Infinity.


   
Top
 
   

 
Java Number Cruncher: The Java Programmer's Guide to Numerical Computing
By Ronald  Mak

Table of Contents
Chapter  3.   The Floating-Point Standard

3.7 Another Look at Roundoff Errors

In Chapter 1, we saw that the float value of graphics/1by3.gif printed as 0.33333334. What happens if we assign the float value to a double variable and then print the double variable's value? Will we get 0.3333333400000000 ? Actually, what we get is 0.3333333432674408. Where did the last eight digits come from? Are they random garbage? Program 3-4 attempts to find some answers. See Listing 3-4.

Listing 3-4 Roundoff errors of the number graphics/1by3.gif .
package numbercruncher.program3_4;

import numbercruncher.mathutils.IEEE754;

/**
 * PROGRAM 3-4: One Third
 *
 * Investigate the floating-point representation of 1/3.
 */
public class OneThird
{
    public static void main (String args[])
    {
        float  fThird     = 1/3f;
        double dConverted = fThird;
        double dThird     = 1/3d;

        System.out.println("          Float 1/3 = " + fThird);
        System.out.println("Converted to double = " + dConverted);
        System.out.println("         Double 1/3 = " + dThird);

        IEEE754 ieeeFThird     = new IEEE754(fThird);
        IEEE754 ieeeDConverted = new IEEE754(dConverted);
        IEEE754 ieeeDThird     = new IEEE754(dThird);

        ieeeFThird.print();
        ieeeDConverted.print();
        ieeeDThird.print();

        // Prepend the leading 0 bits of the converted 1/3.
        int    unbiased = ieeeDConverted.unbiasedExponent();
        String bits     = "1" + ieeeDConverted.fractionBits();
        while (++unbiased < 0) bits = "0" + bits;

        // Sum the indicated negative powers of 2.
        double sum   = 0;
        double power = 0.5;
        for (int i = 0; i < bits.length(); ++i) {
            if (bits.charAt(i) == '1') sum += power;
            power /= 2;
        }

        System.out.println();
        System.out.println("Converted 1/3 by summation = " + sum);
    }
}

Output:

Float 1/3 = 0.33333334
Converted to double = 0.3333333432674408
         Double 1/3 = 0.3333333333333333
------------------------------
float value = 0.33333334
sign=0, exponent=01111101 (biased=125, normalized, unbiased=-2)
significand=1.01010101010101010101011
------------------------------
double value = 0.3333333432674408
sign=0, exponent=01111111101 (biased=1021, normalized, unbiased=-2)
significand=1.0101010101010101010101100000000000000000000000000000
------------------------------
double value = 0.3333333333333333
sign=0, exponent=01111111101 (biased=1021, normalized, unbiased=-2)
significand=1.0101010101010101010101010101010101010101010101010101

Converted 1/3 by summation = 0.3333333432674408

Let's examine graphics/1by3.gif . Its unbiased exponent value is -2, so we need to shift the implied point of its significand two places to the left, giving us the value 0.0101010101010101010101011 in base 2. We can verify it by doing base 2 division:

graphics/03equ23.gif


The IEEE 754 representation of graphics/1by3.gif rounded up the last bit from 0 to 1, thus introducing a very small positive error. We see this error as the final digit 4 (instead of 3) when we print out the float value.

Program 3-4's output also shows what really happens when we convert the float value to a double. This widening operation is exact: It appends 29 (= 53 - 24) zero bits at the right. But when we converted that double value to a decimal number for printing, we didn't get eight decimal zeros at the end; instead, we got what appear to be the garbage digits. For comparison, Program 3-4 also computes and displays the double value of graphics/1by3.gif .

In fact, though, that "garbage" is quite valid, as the latter part of the program demonstrates . Using the binary representation of graphics/1by3.gif , we add the indicated negative powers of 2. The printed sum matches what was printed for the converted value.

As we saw in Chapter 1, a roundoff error occurs when an exact value, such as graphics/1by3.gif , lies between two representable floating-point values. How does Java decide which of the two floating-point values to choose? In the case of the float representation of graphics/1by3.gif , how did Java decide the last bit should be 1 instead of 0?

When an exact values lies between two representable floating-point values, Java picks the floating-point value that is closest to the exact value. If the exact value lies exactly halfway between two floating-point values, Java picks the floating-point value whose least significant (rightmost) bit is 0. This corresponds to the default rounding mode called round to nearest in the IEEE 754 standard.

The IEEE 754 standard defines several other rounding modes for floating-point: round down, round up, and round toward zero. Once again, Java deviates from the standard. Java does not implement the nondefault rounding modes defined by the standard. If you want your Java program to use these other rounding modes, you can write methods that emulate the floating-point operations with the desired modes, or your program can invoke floating-point routines written in other languages.


   
Top