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