Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for November 2, 2004. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). This issue covers: * What's New in the Math Class * Changes in Working With ContentPane These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp. This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Dim Sum Thinking, Inc, and editor-in-chief for java.net (http://java.net). You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/JDCTechTips/2004/tt1102.html. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. For more Java technology content, visit these sites: java.sun.com - The Java technology source for developers. Get the latest Java platform releases, tutorials, newsletters and more. java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together. java.com - The ultimate marketplace promoting Java technology, applications and services. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WHAT'S NEW IN THE MATH CLASS There are ten new methods in the java.lang.Math class and java.lang.StrictMath class in J2SE 5.0. The new methods include convenience methods, hyperbolic trigonometric methods, a common logarithm method, a method for calculating the cube root, and a pair of methods to determine the precision of an answer. This tip introduces each of the new methods. As in the past, use the StrictMath class when you need the results to be identical, that is, bit-for bit, on all platforms. If you don't have this strict requirement, use the methods in the Math class. In fact, many of the Math methods delegate to the corresponding methods in StrictMath.The StrictMath methods obey the contracts of the corresponding Math methods. The delegated methods are allowed to call better-performing implementations that are operating system-specific. This is true provided that the results the implementations produce have the accuracy required by the Math specification (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Math.html). "Units in the last place" (ulps) used to measure the accuracy of the returned result. The ulps are based on ulp(x) where x is the true mathematical answer. The ulp of a real number is the distance between the two floating-point numbers bracketing that value. However, this idealized ulp function can't be implemented. That's because only the numbers representable as floating-point values can be passed in. So the "degenerate" definition of an ulp is used: the distance between the number and the nearest floating-point value that is larger in magnitude. When you call a method in the Math class, it returns an answer that is most often an approximation. The accuracy of the answer depends on the algorithm that is used to produce it. The contract for sqrt() promises that it "returns the correctly rounded positive square root of a double value." "Correctly rounded" means that the floating-point number closest to the true mathematical result must be returned. Consequently, the distance between that value and the nearest floating-point number is at most half the distance between floating-point numbers in that region. Contrast this with the new cbrt() method which returns the cube root of a double. The contract for cbrt() states that "the computed result must be within 1 ulp of the exact result." The cube root function f(x) = x^(1/3) is strictly increasing. A properly monotonic floating-point approximation to a strictly increasing function can, in general, be at most non-decreasing. That's because of the discrete nature and the density of floating-point values. This is true even if the approximation is correctly rounded. For example, take the function y = 0.0000000000001 * x. For adjacent floating-point values x_1 < x_2, it is likely that the floating-point value closest to the exact product (0.0000000000001 * x_1) is the same as the floating-point value closest to the exact product (0.0000000000001 * x_2). Here is a quick sample program that uses cbrt(). By using the new static import feature in J2SE 5.0, you can simply call these methods sqrt() and cbrt() -- you don't have to call them Math.sqrt() and Math.cbrt(). For more information about static imports, see the Tech Tip "Using Static Imports for Constants and Methods ("http://java.sun.com/developer/JDCTechTips/2004/tt1005.html#1"). import static java.lang.Math.*; public class TakeRoots { public static void main(String[] args) { if (args.length != 1) System.exit(0); double x = Double.parseDouble(args[0]); System.out.printf("The square root of %f is %f.\n", x, sqrt(x)); System.out.printf("The cube root of %f is %f.\n", x, cbrt(x)); } } Run the TakeRoots program and pass in the command line parameter 6: java TakeRoots 6 You should get the following results: The square root of 6.000000 is 2.449490 The cube root of 6.000000 is 1.817121 If instead you pass in -6, you should get something like this: The square root of -6.000000 is NaN The cube root of -6.000000 is -1.817121 The result of trying to take a square root of a negative number is NaN (that is, not a number), but the cubed root of a negative number is the negative of the cubed root of the number -- here cbrt(-6) = - cbrt(6). For positive values of x and y, hypot(x,y) returns the length of the hypotenuse of a right triangle with legs of length x and y. Using hypot(x,y) is superior to using sqrt(/x/^2 +/y/^2 ) because hypot avoids intermediate overflow or underflow. Specifically, if at least one of x or y is near sqrt(Double.MAX_VALUE), the square of x or y can overflow to infinity. That's true even though the final result should be representable. The hypot() method is also useful in calculating the Euclidean distance between two points. The log10() method returns the common (base 10) logarithm. You can also calculate the common logarithm using the existing log() method, which returns the natural (base e) logarithm. The conversion is log10(x) = log(x)/log(10). Of course, using the log conversion returns an approximation with additional rounding error. Here is an example that uses hypot(): import static java.lang.Math.*; public class Distance { public static void main(String[] args) { if(args.length !=1) System.exit(0); double x = Double.parseDouble(args[0]); System.out.printf( "hypot(sin( %1$f),cos(%1$f)) = %2$f.\n", x, (float)(hypot(sin(x),cos(x)))); } } Run the Distance program and enter any value as a command line parameter, for example, 3: java Distance 3 The hypot() method returns the double value 1.0, and the displayed result is: hypot(sin(3.000000),cos(3.000000) = 1.000000. You get these results because the standard trigonometry functions sin() and cos() are based on the geometry of the unit circle (x2 + y2 = 1). There are three new trigonometric functions introduced in J2SE 5.0: sinh(), cosh(), and tanh(). These are based on the geometry of the hyperbola (y2 - x2 = 1). The new methods correspond to the hyperbolic sine, hyperbolic cosine, and hyperbolic tangent. The functions are also defined in terms of the Euler constant e: o sinh(x) = (e^x - e^(-x) )/ 2 o cosh(x) = (e^x + e^(-x))/2 o tanh(x) = sinh(x)/cosh(x) These formulas are not, of course, the way in which the calculations of the hyperbolic function values are implemented. Here is a simple program for experimenting with values for the new hyperbolic functions. import static java.lang.Math.*; public class HyperbolicFunctions { public static void main(String[] args) { if (args.length != 1) System.exit(0); double x = Double.parseDouble(args[0]); System.out.printf("The value of sinh(%1$f) is %2$f" + ", and cosh(%1$f) is %3$f.\n", x, sinh(x), cosh(x)); System.out.printf("The tanh(%f) is $f.\n" ,x, tanh(x)); System.out.printf("The value of cosh2(%1$f) - "+ "sinh2(%1$f) is %2$f .\n",x, (float)(pow(cosh(x),2) - pow(sinh(x),2))); } } Run the HyperbolicFunctions program and pass in a command line parameter such as -.5. java HyperbolicFunctions -.5 You should get results that look like the following: The value of sinh(-0.500000) is -0.521095, and cosh(-0.500000) is 1.127626. The tanh(-0.500000) is $f. The value of cosh2(-0.500000) - sinh2(-0.500000) is 1.000000 . Another new method, signum(), can be thought of as returning the sign of a number. Actually, the method returns the value 1.0 for positive arguments, -1.0 for negative arguments, and 0.0 if the argument is zero. Another new method is ulp(). It returns the size of the ulp of a number. Recall that ulp measures the distance from a number to the nearest float that has greater magnitude than the given number. Here is a sample application that uses signum() and ulp(). The example is analogous to entering a number in a calculator and repeatedly pressing the cube root button until there are no visible changes. import static java.lang.Math.*; public class Roots { public static void main(String[] args) { if (args.length != 1) System.exit(0); int count = 1; double x = Double.parseDouble(args[0]); double targetRoot = signum(x); double y = cbrt(x); while ( abs(x - targetRoot) > ulp(x)) { count++; x = y; y = cbrt(x); } System.out.printf("Starting with args[0] it took %d " + "iterations of cbrt() to get %f\n which is within " + "1 ulp of %f .\n", count, x, targetRoot); } } If you run the Roots program and start with -.999999: java Roots -.999999 You should get the following results: Starting with args[0] it took 22 iterations of cbrt() to get -1.000000 which is within 1 ulp of -1.000000 . For more information about the new methods covered in this Tech Tip, see the documentation for java.lang.Math (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Math.html) and java.lang.StrictMath (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/StrictMath.html). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CHANGES IN WORKING WITH CONTENTPANE In previous releases of J2SE, you were not able to directly add components to the top level Swing containers: JFrame, JDialog, JWindow, JApplet, and JInternalFrame. For example , if you had a JFrame instance named myFrame, and you wanted to add a component named someComponent to the JFrame, you needed to call the getContentPane method of JFrame to add the component: myFrame.getContentPane().add(someComponent). In J2SE 5.0, you can add components to the top level Swing containers. You can also directly set the layout manager and remove components from the container. This tip presents the changes to these top level containers that allow you to use add(), setLayout(), and remove() directly without calling getContentPane(). The tip also discusses why you cannot ignore the ContentPane, even though you no longer have to call for it to add components. Let's begin by looking at the situation in J2SE 1.4 and previous releases. Suppose you have a JFrame for which you would like to set the LayoutManager and add various components. If you consult the documentation for JFrame, you will notice that JFrame inherits the add() method from java.awt.Container. The add() method is used to add a component to the JFrame container. The Container class also includes the setLayout() method, which is used to specify the layout manager for the given container. Beginners to Java often conclude that the following program will compile and run properly: import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.GridLayout; import java.awt.Color; public class FrameTest extends JFrame { //Warning - this version is not correct for J2SE 1.4 // it works fine with J2SE 5.0 FrameTest(){ super("Test Frame"); setBackground(Color.BLACK); setLayout(new GridLayout(7,7,2,2)); // problem 1 for ( int i=0;i<49; i++){ JPanel jPanel = new JPanel(); jPanel.setBackground(new Color ( (int)(Math.random()*16777215))); add(jPanel); // problem 2 } setSize(400,400); setVisible(true); } public static void main(String[] args){ new FrameTest(); } } This code does compile and run in J2SE 5.0. It also compiles in J2SE 1.4. But there are problems running the code in J2SE 1.4. The lines marked as problem 1 and problem 2 are calls to the methods setLayout() and add() in JFrame. If you compile the code and then run it in J2SE 1.4, you will encounter the following runtime exception: Exception in thread "main" java.lang.Error: Do not use FrameTest.setLayout() use FrameTest.getContentPane().setLayout() instead at javax.swing.JFrame.createRootPaneException(JFrame.java:465) at javax.swing.JFrame.setLayout(JFrame.java:531) at FrameTest.(FrameTest.java:11) at FrameTest.main(FrameTest.java:23) If you follow the advice given by the exception and replace the setLayout line, labeled above as "problem 1", with the following line: getContentPane().setLayout(new GridLayout(7,7,2,2)); and then recompile and rerun FrameTest in J2SE 1.4, you will get a second runtime exception. You can fix the second exception by replacing the add(jPanel) line, the line labeled "problem 2", with the following line: getContentPane().add(jPanel); Recompile and run FrameTest in J2SE 1.4. You will see a JFrame which contains a seven-by-seven grid of differently colored JPanels. The previous example illustrated that prior to J2SE 5.0, you had to explicitly call getContentPane() and call add() or setLayout() on the Container object returned by getContentPane(). With the J2SE 5.0 release, you can call add() or setLayout() directly on the Container object. For example, if myFrame is an instance of JFrame, you can replace myFrame.getContentPane().add() with the simpler myFrame.add(). It might be helpful to remember that the implementation of add() is effectively this.getContentPane().add(). The same is true for the setLayout() and remove() methods. In fact, the same is true for all top level Swing components: JFrame, JDialog, JWindow, JApplet, and JInternalFrame. Compile and run the original version of FrameTest using J2SE 5.0. Again, you should see a grid of forty-nine JPanels. Because the background colors are assigned randomly to the JPanels, the colors of the JPanels in this run will differ from those you saw in the previous run. You might notice a subtle problem. The following line in the FrameTest program doesn't seem to have any effect: setBackground(Color.BLACK); You would expect the background of the JFrame to be colored black so that the grid lines in between the colored JPanels should be black and not grey. The background of the JFrame is colored black, but it is not ordinarily visible. That's because the content pane sits on top of the region that is black. If you resize the JFrame you might momentarily see the black background. Instead, change the setBackground line in the FrameTest program to the following: getContentPane().setBackground(Color.BLACK); Now you should see that the grid lines are colored black. The reason for drawing attention to this point is to help you realize that although you do not need to explicitly call getContentPane() when using the add(), remove(), and setLayout() methods in JFrame, you can not ignore the role of the ContentPane. To learn more about changes in Swing for J2SE 5.0, join the following online chat: Nov. 9 11:00 A.M. PST/19:00 UTC What's New in Swing? (http://java.sun.com/developer/community/chat/JavaLive/index.html) . . . . . . . . . . . . . . . . . . . . . . . PRIVACY STATEMENT: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your e-mail preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe. Please read our Terms of Use and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the JDC Newsletters and Publications page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/TechTips/index.html - COPYRIGHT Copyright 2004 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html Core Java Technologies Tech Tips November 2, 2004 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.