You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ps...@apache.org on 2005/11/06 19:07:11 UTC

svn commit: r331126 - in /jakarta/commons/proper/math/branches/MATH_1_1/src: java/org/apache/commons/math/complex/Complex.java test/org/apache/commons/math/complex/ComplexTest.java

Author: psteitz
Date: Sun Nov  6 10:06:46 2005
New Revision: 331126

URL: http://svn.apache.org/viewcvs?rev=331126&view=rev
Log:
Improved consistency, documentation and test cases for Complex arithemetic.
PR# 37086

Modified:
    jakarta/commons/proper/math/branches/MATH_1_1/src/java/org/apache/commons/math/complex/Complex.java
    jakarta/commons/proper/math/branches/MATH_1_1/src/test/org/apache/commons/math/complex/ComplexTest.java

Modified: jakarta/commons/proper/math/branches/MATH_1_1/src/java/org/apache/commons/math/complex/Complex.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/math/branches/MATH_1_1/src/java/org/apache/commons/math/complex/Complex.java?rev=331126&r1=331125&r2=331126&view=diff
==============================================================================
--- jakarta/commons/proper/math/branches/MATH_1_1/src/java/org/apache/commons/math/complex/Complex.java (original)
+++ jakarta/commons/proper/math/branches/MATH_1_1/src/java/org/apache/commons/math/complex/Complex.java Sun Nov  6 10:06:46 2005
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2004 The Apache Software Foundation.
+ * Copyright 2003-2005 The Apache Software Foundation.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,16 @@
 /**
  * Representation of a Complex number - a number which has both a 
  * real and imaginary part.
+ * <p>
+ * Implementation of arithmetic operations handles <code>NaN</code> and
+ * infinite values according to the rules for {@link java.lang.Double}
+ * arithmetic, applying definitional formulas and returning <code>NaN</code> or
+ * infinite values in real or imaginary parts as these arise in computation. 
+ * See the javadoc for individual methods for details.
+ * <p>
+ * {@link #equals} identifies all values with <code>NaN</code> in either real 
+ * or imaginary part - e.g., <pre>
+ * <code>1 + NaNi  == NaN + i == NaN + NaNi.</code></pre>
  *
  * @author Apache Software Foundation
  * @version $Revision$ $Date$
@@ -40,6 +50,9 @@
     /** A complex number representing "1.0 + 0.0i" */    
     public static final Complex ONE = new Complex(1.0, 0.0);
     
+    /** A complex number representing "0.0 + 0.0i" */    
+    public static final Complex ZERO = new Complex(0.0, 0.0);
+    
     /** The imaginary part. */
     protected double imaginary;
     
@@ -60,6 +73,11 @@
 
     /**
      * Return the absolute value of this complex number.
+     * <p>
+     * Returns <code>NaN</code> if either real or imaginary part is
+     * <code>NaN</code> and <code>Double.POSITIVE_INFINITY</code> if
+     * neither part is <code>NaN</code>, but at least on part takes an infinite
+     * value.
      *
      * @return the absolute value.
      */
@@ -67,6 +85,11 @@
         if (isNaN()) {
             return Double.NaN;
         }
+        
+        if (isInfinite()) {
+            return Double.POSITIVE_INFINITY;
+        }
+        
         if (Math.abs(real) < Math.abs(imaginary)) {
             if (imaginary == 0.0) {
                 return Math.abs(real);
@@ -84,38 +107,77 @@
     
     /**
      * Return the sum of this complex number and the given complex number.
+     * <p>
+     * Uses the definitional formula 
+     * <pre>
+     * (a + bi) + (c + di) = (a+c) + (b + d)i
+     * </pre>
+     * <p>
+     * Inifinite and NaN values are returned in the parts according to the
+     * rules for {@link java.lang.Double} arithmetic. 
      *
      * @param rhs the other complex number.
      * @return the complex number sum.
      */
-    public Complex add(Complex rhs) {
-        if (isNaN() || rhs.isNaN()) {
-            return NaN;
-        }
-        
+    public Complex add(Complex rhs) {   
         return new Complex(real + rhs.getReal(),
             imaginary + rhs.getImaginary());
     }
     
     /**
-     * Return the conjugate of this complex number.  The conjugate of
-     * "A + Bi" is "A - Bi".  Complex.NaN is returned if either the real or imaginary part of 
-     * this Complex number equals Double.NaN.
+     * Return the conjugate of this complex number. The conjugate of
+     * "A + Bi" is "A - Bi". 
+     * <p>
+     * Complex.NaN is returned if either the real or imaginary
+     * part of this Complex number equals Double.NaN.
+     * <p>
+     * If the imaginary part is infinite, and the real part is not NaN, 
+     * the returned value has infinite imaginary part of the opposite
+     * sign - e.g. the conjugate of <code>1 + POSITIVE_INFINITY i</code>
+     * is <code>1 + NEGATIVE_INFINITY i</code>
      *
      * @return the conjugate of this Complex object
      */
     public Complex conjugate() {
         if (isNaN()) {
             return NaN;
-        }
-        
+        }   
         return new Complex(real, -imaginary);
     }
     
     /**
      * Return the quotient of this complex number and the given complex number.
-     * @param rhs the other complex number.
-     * @return the complex number quotient.
+     * <p>
+     * Implements the definitional formula
+     * <pre><code>
+     *    a + bi          ac + bd + (bc - ad)i
+     *    ----------- = -------------------------
+     *    c + di                c*c + d*d
+     * </code></pre>
+     * but uses 
+     * <a href="http://doi.acm.org/10.1145/1039813.1039814">
+     * prescaling of operands</a> to limit the effects of overflows and
+     * underflows in the computation.
+     * <p>
+     * Infinite and NaN values are handled / returned according to the
+     * following rules, applied in the order presented:
+     * <ul>
+     * <li>If either this or <code>rhs</code> has a NaN value in either part,
+     *  {@link #NaN} is returned.</li>
+     * <li>If <code>rhs</code> equals {@link #ZERO}, {@link #NaN} is returned.
+     * </li>
+     * <li>If this and <code>rhs</code> are both infinite,
+     * {@link #NaN} is returned.</li>
+     * <li>If this is finite (i.e., has no infinite or NaN parts) and
+     *  <code>rhs</code> is infinite (one or both parts infinite), 
+     * {@link #ZERO} is returned.</li>
+     * <li>If this is infinite and <code>rhs</code> is finite, NaN values are
+     * returned in parts if the {@link java.lang.Double} rules applied to the
+     * definitional formula force NaN results.</li>
+     * </ul>
+     * 
+     * @param rhs the other complex number
+     * @return the complex number quotient
      */
     public Complex divide(Complex rhs) {
         if (isNaN() || rhs.isNaN()) {
@@ -125,7 +187,11 @@
         double c = rhs.getReal();
         double d = rhs.getImaginary();
         if (c == 0.0 && d == 0.0) {
-            throw new ArithmeticException("Error: division by zero.");
+            return NaN;
+        }
+        
+        if (rhs.isInfinite() && !isInfinite()) {
+            return ZERO;
         }
 
         if (Math.abs(c) < Math.abs(d)) {
@@ -162,7 +228,7 @@
      * @param other Object to test for equality to this
      * @return true if two Complex objects are equal, false if
      *         object is null, not an instance of Complex, or
-     *         not equal to this Complex instance.
+     *         not equal to this Complex instance
      * 
      */
     public boolean equals(Object other) {
@@ -226,19 +292,50 @@
     }
     
     /**
-     * Returns true if this complex number is the special Not-a-Number (NaN)
-     * value.
+     * Returns true if this complex number is equal to the special 
+     * Not-a-Number (NaN) value.  
      *
-     * @return true if the value represented by this object is NaN; false
-     *         otherwise.
+     * @return  true if either or both parts of this complex number take
+     * NaN values; false otherwise.
      */
     public boolean isNaN() {
         return Double.isNaN(real) || Double.isNaN(imaginary);        
     }
     
     /**
+     * Returns true if either the real or imaginary part of this complex number
+     * takes an infinite value (either <code>Double.POSITIVE_INFINITY</code> or 
+     * <code>Double.NEGATIVE_INFINITY</code>) and neither part
+     * is <code>NaN</code>.
+     * 
+     * @return true if one or both parts of this complex number are infinite
+     * and neither part is <code>NaN</code>
+     */
+    public boolean isInfinite() {
+        return !isNaN() && 
+        (Double.isInfinite(real) || Double.isInfinite(imaginary));        
+    }
+    
+    /**
      * Return the product of this complex number and the given complex number.
-     *
+     * <p>
+     * Implements the definitional formula:
+     * <pre><code>
+     * (a + bi)(c + di) = (ac - bd) + (ad + bc)i
+     * </code></pre>
+     * <p>
+     * Returns {@link #NaN} if either this or <code>rhs</code> has one or more
+     * NaN parts.
+     * <p>
+     * Returns NaN or infintite values in components of the result per the
+     * definitional formula and and the rules for {@link java.lang.Double}
+     * arithmetic.  Examples:
+     * <pre><code>
+     *  (1 + i) (INF + i)  =  INF + INFi
+     *  (1 + INFi) (1 - INFi) = INF + NaNi
+     *  (-INF + -INFi)(1 + NaNi) = NaN + NaNi
+     *  </code></pre>
+     * 
      * @param rhs the other complex number.
      * @return the complex number product.
      */
@@ -246,11 +343,8 @@
         if (isNaN() || rhs.isNaN()) {
             return NaN;
         }
-        
-        double p = (real + imaginary) * (rhs.getReal() + rhs.getImaginary());
-        double ac = real * rhs.getReal();
-        double bd = imaginary * rhs.getImaginary();
-        return new Complex(ac - bd, p - ac - bd);
+        return new Complex(real * rhs.real - imaginary * rhs.imaginary,
+                real * rhs.imaginary + imaginary * rhs.real);
     }
     
     /**

Modified: jakarta/commons/proper/math/branches/MATH_1_1/src/test/org/apache/commons/math/complex/ComplexTest.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/math/branches/MATH_1_1/src/test/org/apache/commons/math/complex/ComplexTest.java?rev=331126&r1=331125&r2=331126&view=diff
==============================================================================
--- jakarta/commons/proper/math/branches/MATH_1_1/src/test/org/apache/commons/math/complex/ComplexTest.java (original)
+++ jakarta/commons/proper/math/branches/MATH_1_1/src/test/org/apache/commons/math/complex/ComplexTest.java Sun Nov  6 10:06:46 2005
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2004 The Apache Software Foundation.
+ * Copyright 2003-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +23,16 @@
  */
 public class ComplexTest extends TestCase {
     
+    private double inf = Double.POSITIVE_INFINITY;
+    private double neginf = Double.NEGATIVE_INFINITY;
+    private double nan = Double.NaN;
+    private Complex oneInf = new Complex(1, inf);
+    private Complex oneNegInf = new Complex(1, neginf);
+    private Complex infOne = new Complex(inf, 1);
+    private Complex negInfInf = new Complex(neginf, inf);
+    private Complex negInfNegInf = new Complex(neginf, neginf);
+    private Complex oneNaN = new Complex(1, nan);
+    
     public void testConstructor() {
         Complex z = new Complex(3.0, 4.0);
         assertEquals(3.0, z.getReal(), 1.0e-5);
@@ -33,7 +43,7 @@
         Complex z = new Complex(3.0, Double.NaN);
         assertTrue(z.isNaN());
 
-        z = new Complex(Double.NaN, 4.0);
+        z = new Complex(nan, 4.0);
         assertTrue(z.isNaN());
 
         z = new Complex(3.0, 4.0);
@@ -47,6 +57,17 @@
     
     public void testAbsNaN() {
         assertTrue(Double.isNaN(Complex.NaN.abs()));
+        Complex z = new Complex(inf, nan);
+        assertTrue(Double.isNaN(z.abs()));
+    }
+    
+    public void testAbsInfinite() {
+        Complex z = new Complex(inf, 0);
+        assertEquals(inf, z.abs(), 0);
+        z = new Complex(0, neginf);
+        assertEquals(inf, z.abs(), 0);
+        z = new Complex(inf, neginf);
+        assertEquals(inf, z.abs(), 0);     
     }
     
     public void testAdd() {
@@ -61,6 +82,21 @@
         Complex x = new Complex(3.0, 4.0);
         Complex z = x.add(Complex.NaN);
         assertTrue(z.isNaN());
+        z = new Complex(1, nan);
+        Complex w = x.add(z);
+        assertEquals(w.real, 4.0, 0);
+        assertTrue(Double.isNaN(w.imaginary));
+    }
+    
+    public void testAddInfinite() {
+        Complex x = new Complex(1, 1);
+        Complex z = new Complex(inf, 0);
+        Complex w = x.add(z);
+        assertEquals(w.imaginary, 1, 0);
+        assertEquals(inf, w.real, 0);
+        
+        x = new Complex(neginf, 0);
+        assertTrue(Double.isNaN(x.add(z).real));
     }
     
     public void testConjugate() {
@@ -75,6 +111,13 @@
         assertTrue(z.isNaN());
     }
     
+    public void testConjugateInfiinite() {
+        Complex z = new Complex(0, inf);
+        assertEquals(neginf, z.conjugate().imaginary, 0);
+        z = new Complex(0, neginf);
+        assertEquals(inf, z.conjugate().imaginary, 0);
+    }
+    
     public void testDivide() {
         Complex x = new Complex(3.0, 4.0);
         Complex y = new Complex(5.0, 6.0);
@@ -83,12 +126,46 @@
         assertEquals(2.0 / 61.0, z.getImaginary(), 1.0e-5);
     }
     
+    public void testDivideInfinite() {
+        Complex x = new Complex(3, 4);
+        Complex w = new Complex(neginf, inf);
+        assertTrue(x.divide(w).equals(Complex.ZERO));
+        
+        Complex z = w.divide(x);
+        assertTrue(Double.isNaN(z.real));
+        assertEquals(inf, z.imaginary, 0);
+        
+        w = new Complex(inf, inf);
+        z = w.divide(x);
+        assertTrue(Double.isNaN(z.imaginary));
+        assertEquals(inf, z.real, 0);
+        
+        w = new Complex(1, inf);
+        z = w.divide(w);
+        assertTrue(Double.isNaN(z.real));
+        assertTrue(Double.isNaN(z.imaginary));
+    }
+    
     public void testDivideNaN() {
         Complex x = new Complex(3.0, 4.0);
         Complex z = x.divide(Complex.NaN);
         assertTrue(z.isNaN());
     }
     
+    public void testDivideNaNInf() {  
+       Complex z = oneInf.divide(Complex.ONE);
+       assertTrue(Double.isNaN(z.real));
+       assertEquals(inf, z.imaginary, 0);
+       
+       z = negInfNegInf.divide(oneNaN);
+       assertTrue(Double.isNaN(z.real));
+       assertTrue(Double.isNaN(z.imaginary));
+       
+       z = negInfInf.divide(Complex.ONE);
+       assertTrue(Double.isNaN(z.real));
+       assertTrue(Double.isNaN(z.imaginary));
+    }
+    
     public void testMultiply() {
         Complex x = new Complex(3.0, 4.0);
         Complex y = new Complex(5.0, 6.0);
@@ -101,6 +178,21 @@
         Complex x = new Complex(3.0, 4.0);
         Complex z = x.multiply(Complex.NaN);
         assertTrue(z.isNaN());
+    }
+    
+    public void testMultiplyNaNInf() {
+        Complex z = new Complex(1,1);
+        Complex w = z.multiply(infOne);
+        assertEquals(w.real, inf, 0);
+        assertEquals(w.imaginary, inf, 0);
+        
+        w = oneInf.multiply(oneNegInf);
+        assertEquals(w.real, inf, 0);
+        assertTrue(Double.isNaN(w.imaginary));
+        
+        w = negInfNegInf.multiply(oneNaN);
+        assertTrue(Double.isNaN(w.real));
+        assertTrue(Double.isNaN(w.imaginary));  
     }
     
     public void testNegate() {



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org