You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by ga...@apache.org on 2012/05/29 17:55:58 UTC

svn commit: r1343794 - in /xmlgraphics/commons/trunk: src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java

Author: gadams
Date: Tue May 29 15:55:58 2012
New Revision: 1343794

URL: http://svn.apache.org/viewvc?rev=1343794&view=rev
Log:
Bugzilla #43940: Fix handling of NaN, {+,-}Infinity, and other edge cases. Submitted by Julien Aymé.

Modified:
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java

Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java?rev=1343794&r1=1343793&r2=1343794&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/util/DoubleFormatUtil.java Tue May 29 15:55:58 2012
@@ -79,6 +79,10 @@ public class DoubleFormatUtil {
             // Will always be rounded to 0
             target.append('0');
             return;
+        } else if (Double.isNaN(source) || Double.isInfinite(source)) {
+            // Cannot be formated
+            target.append(Double.toString(source));
+            return;
         }
 
         boolean negative = source < 0.0;
@@ -92,7 +96,7 @@ public class DoubleFormatUtil {
         // The only way to format precisely the double is to use the String
         // representation of the double, and then to do mathematical integer operation on it.
         String s = Double.toString(source);
-        if (source >= 10e-3 && source < 1e7) {
+        if (source >= 1e-3 && source < 1e7) {
             // Plain representation of double: "intPart.decimalPart"
             int dot = s.indexOf('.');
             String decS = s.substring(dot + 1);
@@ -103,6 +107,10 @@ public class DoubleFormatUtil {
                     target.append(s.substring(0, dot));
                 } else {
                     target.append(s);
+                    // Remove trailing zeroes
+                    for (int l = target.length() - 1; l >= 0 && target.charAt(l) == '0'; l--) {
+                        target.setLength(l);
+                    }
                 }
                 return;
             } else if (scale + 1 < decLength) {
@@ -116,7 +124,9 @@ public class DoubleFormatUtil {
         } else {
             // Scientific representation of double: "x.xxxxxEyyy"
             int dot = s.indexOf('.');
+            assert dot >= 0;
             int exp = s.indexOf('E');
+            assert exp >= 0;
             int exposant = Integer.parseInt(s.substring(exp + 1));
             String intS = s.substring(0, dot);
             String decS = s.substring(dot + 1, exp);
@@ -155,7 +165,8 @@ public class DoubleFormatUtil {
                     long decP = Long.parseLong(intS);
                     format(target, scale, 0L, decP);
                 } else {
-                    long decP = Long.parseLong(intS) * tenPow(digits) + Long.parseLong(decS.substring(0, Math.min(decLength, digits)));
+                    long subDecP = decLength <= digits ? Long.parseLong(decS) * tenPow(digits - decLength) : Long.parseLong(decS.substring(0, digits));
+                    long decP = Long.parseLong(intS) * tenPow(digits) + subDecP;
                     format(target, scale, 0L, decP);
                 }
             }
@@ -177,12 +188,16 @@ public class DoubleFormatUtil {
     /**
      * Most used power of ten (to avoid the cost of Math.pow(10, n)
      */
-    private static final long[] tenPows = new long[20];
+    private static final long[] tenPows = new long[19];
+    private static final double[] tenPowsDouble = new double[21];
     static {
         tenPows[0] = 1L;
         for (int i = 1; i < tenPows.length; i++) {
             tenPows[i] = tenPows[i - 1] * 10L;
         }
+        for (int i = 0; i < tenPowsDouble.length; i++) {
+            tenPowsDouble[i] = Double.parseDouble("1e" + i);
+        }
     }
 
     /**
@@ -196,6 +211,11 @@ public class DoubleFormatUtil {
         return n < tenPows.length ? tenPows[n] : (long) Math.pow(10, n);
     }
 
+    private static double tenPowDouble(int n) {
+        assert n >= 0;
+        return n < tenPowsDouble.length ? tenPowsDouble[n] : Math.pow(10, n);
+    }
+
     /**
      * Helper method to do the custom rounding used within formatDoublePrecise
      * 
@@ -205,15 +225,14 @@ public class DoubleFormatUtil {
      * @param decP the source decimal part, truncated to scale + 1 digit 
      */
     private static void format(StringBuffer target, int scale, long intP, long decP) {
-        long scaleTen = tenPow(scale);
         if (decP != 0L) {
             // decP is the decimal part of source, truncated to scale + 1 digit.
             // Custom rounding: add 5
             decP += 5L;
             decP /= 10L;
-            if (decP >= scaleTen) {
+            if (decP >= tenPowDouble(scale)) {
                 intP++;
-                decP -= scaleTen;
+                decP -= tenPow(scale);
             }
             if (decP != 0L) {
                 // Remove trailing zeroes
@@ -226,7 +245,7 @@ public class DoubleFormatUtil {
         target.append(intP);
         if (decP != 0L) {
             target.append('.');
-            while (scale > 0 && decP < tenPow(--scale)) {
+            while (scale > 0 && decP < tenPowDouble(--scale)) {
                 // Insert leading zeroes
                 target.append('0');
             }
@@ -251,6 +270,10 @@ public class DoubleFormatUtil {
             // Will always be rounded to 0
             target.append('0');
             return;
+        } else if (Double.isNaN(source) || Double.isInfinite(source)) {
+            // Cannot be formated
+            target.append(Double.toString(source));
+            return;
         }
 
         boolean isPositive = source >= 0.0;
@@ -318,7 +341,8 @@ public class DoubleFormatUtil {
      * @return true if the rounding will potentially use too many digits
      */
     private static boolean tooManyDigitsUsed(double source, int scale) {
-        return getExponant(source) + scale >= 14;
+        // if scale >= 19, 10^19 > Long.MAX_VALUE
+        return scale >= 19 || getExponant(source) + scale >= 14;
     }
 
     /**

Modified: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java?rev=1343794&r1=1343793&r2=1343794&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java (original)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/util/DoubleFormatUtilTest.java Tue May 29 15:55:58 2012
@@ -118,6 +118,102 @@ public class DoubleFormatUtilTest extend
         expected = "0.00002";
         actual = format(value, decimals, precision);
         assertEquals(value, decimals, precision, expected, actual);
+
+        // Test added after bug #43940 was reopened
+        value = 0.005859375;
+        expected = "0.00585938";
+        actual = format(value, 8, 8);
+        assertEquals(value, 8, 8, expected, actual);
+    }
+
+    public void testLimits() {
+        int decimals = 19;
+        int precision = 19;
+
+        double value = Double.NaN;
+        String expected = "NaN";
+        String actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = Double.POSITIVE_INFINITY;
+        expected = "Infinity";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = Double.NEGATIVE_INFINITY;
+        expected = "-Infinity";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1e-3 + Double.MIN_VALUE;
+        expected = "0.001";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1e-3 - Double.MIN_VALUE;
+        expected = "0.001";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1e-3;
+        expected = "0.001";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 0.0010000000000000002; // == Math.nextAfter(1e-3, Double.POSITIVE_INFINITY);
+        expected = "0.0010000000000000002";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+        expected = "0.001";
+        actual = format(value, 18, 18);
+        assertEquals(value, 18, 18, expected, actual);
+
+        value = 0.0009999999999999998; // == Math.nextAfter(1e-3, Double.NEGATIVE_INFINITY);
+        expected = "0.0009999999999999998";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+        expected = "0.001";
+        actual = format(value, 18, 18);
+        assertEquals(value, 18, 18, expected, actual);
+
+        value = 1e7 + Double.MIN_VALUE;
+        expected = "10000000";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1e7 - Double.MIN_VALUE;
+        expected = "10000000";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1e7;
+        expected = "10000000";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+
+        value = 1.0000000000000002E7; // == Math.nextAfter(1e7, Double.POSITIVE_INFINITY);
+        expected = "10000000.000000002";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+        expected = "10000000";
+        actual = format(value, 8, 8);
+        assertEquals(value, 8, 8, expected, actual);
+
+        value = 9999999.999999998; // == Math.nextAfter(1e7, Double.NEGATIVE_INFINITY);
+        expected = "9999999.999999998";
+        actual = format(value, decimals, precision);
+        assertEquals(value, decimals, precision, expected, actual);
+        expected = "10000000";
+        actual = format(value, 8, 8);
+        assertEquals(value, 8, 8, expected, actual);
+
+        value = 0.000009999999999999997; // Check higher precision
+        expected = "0.000009999999999999997";
+        actual = format(value, 21, 21);
+        assertEquals(value, 21, 21, expected, actual);
+        expected = "0.00001";
+        actual = format(value, 20, 20);
+        assertEquals(value, 20, 20, expected, actual);
     }
 
     /**



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org