summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/poi/ss/format/CellNumberFormatter.java124
-rw-r--r--src/java/org/apache/poi/ss/format/SimpleFraction.java170
-rw-r--r--src/java/org/apache/poi/ss/usermodel/FractionFormat.java104
-rw-r--r--src/testcases/org/apache/poi/ss/format/TestCellFormat.java11
4 files changed, 188 insertions, 221 deletions
diff --git a/src/java/org/apache/poi/ss/format/CellNumberFormatter.java b/src/java/org/apache/poi/ss/format/CellNumberFormatter.java
index 91f0979a0b..d2b9100556 100644
--- a/src/java/org/apache/poi/ss/format/CellNumberFormatter.java
+++ b/src/java/org/apache/poi/ss/format/CellNumberFormatter.java
@@ -824,7 +824,7 @@ public class CellNumberFormatter extends CellFormatter {
n = (int) Math.round(fractional);
d = 1;
} else {
- Fraction frac = new Fraction(fractional, maxDenominator);
+ SimpleFraction frac = SimpleFraction.buildFractionMaxDenominator(fractional, maxDenominator);
n = frac.getNumerator();
d = frac.getDenominator();
}
@@ -971,128 +971,6 @@ public class CellNumberFormatter extends CellFormatter {
SIMPLE_NUMBER.formatValue(toAppendTo, value);
}
- /**
- * Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
- * YK: The only reason of having this inner class is to avoid dependency on the Commons-Math jar.
- */
- private static class Fraction {
- /** The denominator. */
- private final int denominator;
-
- /** The numerator. */
- private final int numerator;
-
- /**
- * Create a fraction given the double value and either the maximum error
- * allowed or the maximum number of denominator digits.
- *
- * @param value the double value to convert to a fraction.
- * @param epsilon maximum error allowed. The resulting fraction is within
- * <code>epsilon</code> of <code>value</code>, in absolute terms.
- * @param maxDenominator maximum denominator value allowed.
- * @param maxIterations maximum number of convergents
- * @throws RuntimeException if the continued fraction failed to
- * converge.
- */
- private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
- {
- long overflow = Integer.MAX_VALUE;
- double r0 = value;
- long a0 = (long)Math.floor(r0);
- if (a0 > overflow) {
- throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
- }
-
- // check for (almost) integer arguments, which should not go
- // to iterations.
- if (Math.abs(a0 - value) < epsilon) {
- this.numerator = (int) a0;
- this.denominator = 1;
- return;
- }
-
- long p0 = 1;
- long q0 = 0;
- long p1 = a0;
- long q1 = 1;
-
- long p2;
- long q2;
-
- int n = 0;
- boolean stop = false;
- do {
- ++n;
- double r1 = 1.0 / (r0 - a0);
- long a1 = (long)Math.floor(r1);
- p2 = (a1 * p1) + p0;
- q2 = (a1 * q1) + q0;
- if ((p2 > overflow) || (q2 > overflow)) {
- throw new RuntimeException("Overflow trying to convert "+value+" to fraction ("+p2+"/"+q2+")");
- }
-
- double convergent = (double)p2 / (double)q2;
- if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
- p0 = p1;
- p1 = p2;
- q0 = q1;
- q1 = q2;
- a0 = a1;
- r0 = r1;
- } else {
- stop = true;
- }
- } while (!stop);
-
- if (n >= maxIterations) {
- throw new RuntimeException("Unable to convert "+value+" to fraction after "+maxIterations+" iterations");
- }
- if (q2 < maxDenominator) {
- this.numerator = (int) p2;
- this.denominator = (int) q2;
- } else {
- this.numerator = (int) p1;
- this.denominator = (int) q1;
- }
-
- }
-
- /**
- * Create a fraction given the double value and maximum denominator.
- * <p>
- * References:
- * <ul>
- * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
- * Continued Fraction</a> equations (11) and (22)-(26)</li>
- * </ul>
- * </p>
- * @param value the double value to convert to a fraction.
- * @param maxDenominator The maximum allowed value for denominator
- * @throws RuntimeException if the continued fraction failed to
- * converge
- */
- public Fraction(double value, int maxDenominator)
- {
- this(value, 0, maxDenominator, 100);
- }
-
- /**
- * Access the denominator.
- * @return the denominator.
- */
- public int getDenominator() {
- return denominator;
- }
-
- /**
- * Access the numerator.
- * @return the numerator.
- */
- public int getNumerator() {
- return numerator;
- }
-
- }
}
diff --git a/src/java/org/apache/poi/ss/format/SimpleFraction.java b/src/java/org/apache/poi/ss/format/SimpleFraction.java
new file mode 100644
index 0000000000..1452b520f7
--- /dev/null
+++ b/src/java/org/apache/poi/ss/format/SimpleFraction.java
@@ -0,0 +1,170 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+package org.apache.poi.ss.format;
+
+public class SimpleFraction {
+
+
+ /** The denominator. */
+ private final int denominator;
+
+ /** The numerator. */
+ private final int numerator;
+ /**
+ * Create a fraction given a double value and a denominator.
+ *
+ * @param val double value of fraction
+ * @param exactDenom the exact denominator
+ * @return
+ */
+ public static SimpleFraction buildFractionExactDenominator(double val, int exactDenom){
+ int num = (int)Math.round(val*(double)exactDenom);
+ return new SimpleFraction(num,exactDenom);
+ }
+
+ /**
+ * Create a fraction given the double value and either the maximum error
+ * allowed or the maximum number of denominator digits.
+ *
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator maximum denominator value allowed.
+ *
+ * @throws RuntimeException if the continued fraction failed to
+ * converge.
+ * @throws IllegalArgumentException if value > Integer.MAX_VALUE
+ */
+ public static SimpleFraction buildFractionMaxDenominator(double value, int maxDenominator){
+ return buildFractionMaxDenominator(value, 0, maxDenominator, 100);
+ }
+ /**
+ * Create a fraction given the double value and either the maximum error
+ * allowed or the maximum number of denominator digits.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ * Continued Fraction</a> equations (11) and (22)-(26)</li>
+ * </ul>
+ * </p>
+ *
+ * Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
+ * YK: The only reason of having this class is to avoid dependency on the Commons-Math jar.
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within
+ * <code>epsilon</code> of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws RuntimeException if the continued fraction failed to
+ * converge.
+ * @throws IllegalArgumentException if value > Integer.MAX_VALUE
+ */
+ private static SimpleFraction buildFractionMaxDenominator(double value, double epsilon, int maxDenominator, int maxIterations)
+ {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long)Math.floor(r0);
+ if (a0 > overflow) {
+ throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (Math.abs(a0 - value) < epsilon) {
+ return new SimpleFraction((int)a0, 1);
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2;
+ long q2;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ double r1 = 1.0 / (r0 - a0);
+ long a1 = (long)Math.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+ //MATH-996/POI-55419
+ if (epsilon == 0.0f && maxDenominator > 0 && Math.abs(q2) > maxDenominator &&
+ Math.abs(q1) < maxDenominator){
+
+ return new SimpleFraction((int)p1, (int)q1);
+ }
+ if ((p2 > overflow) || (q2 > overflow)) {
+ throw new RuntimeException("Overflow trying to convert "+value+" to fraction ("+p2+"/"+q2+")");
+ }
+
+ double convergent = (double)p2 / (double)q2;
+ if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new RuntimeException("Unable to convert "+value+" to fraction after "+maxIterations+" iterations");
+ }
+
+ if (q2 < maxDenominator) {
+ return new SimpleFraction((int) p2, (int)q2);
+ } else {
+ return new SimpleFraction((int)p1, (int)q1);
+ }
+
+ }
+
+ /**
+ * Create a fraction given a numerator and denominator.
+ * @param numerator
+ * @param denominator maxDenominator The maximum allowed value for denominator
+ */
+ public SimpleFraction(int numerator, int denominator)
+ {
+ this.numerator = numerator;
+ this.denominator = denominator;
+ }
+
+ /**
+ * Access the denominator.
+ * @return the denominator.
+ */
+ public int getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the numerator.
+ * @return the numerator.
+ */
+ public int getNumerator() {
+ return numerator;
+ }
+
+}
+
diff --git a/src/java/org/apache/poi/ss/usermodel/FractionFormat.java b/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
index 1522e7afb8..43564c8854 100644
--- a/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
+++ b/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
@@ -22,6 +22,7 @@ import java.text.ParsePosition;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.poi.ss.format.SimpleFraction;
import org.apache.poi.ss.formula.eval.NotImplementedException;
/**
@@ -35,7 +36,7 @@ import org.apache.poi.ss.formula.eval.NotImplementedException;
* If further uses for Commons Math are found, we will consider adding it as a dependency.
* For now, we have in-lined the one method to keep things simple.</p>
*/
-/* One question remains...is the value of epsilon in calcFractionMaxDenom reasonable? */
+
@SuppressWarnings("serial")
public class FractionFormat extends Format {
private final static Pattern DENOM_FORMAT_PATTERN = Pattern.compile("(?:(#+)|(\\d+))");
@@ -113,7 +114,6 @@ public class FractionFormat extends Format {
}
//this is necessary to prevent overflow in the maxDenom calculation
- //stink1
if (wholePart+(int)decPart == wholePart+decPart){
StringBuilder sb = new StringBuilder();
@@ -128,11 +128,11 @@ public class FractionFormat extends Format {
try{
//this should be the case because of the constructor
if (exactDenom > 0){
- fract = calcFractionExactDenom(decPart, exactDenom);
+ fract = SimpleFraction.buildFractionExactDenominator(decPart, exactDenom);
} else {
- fract = calcFractionMaxDenom(decPart, maxDenom);
+ fract = SimpleFraction.buildFractionMaxDenominator((double)decPart, maxDenom);
}
- } catch (SimpleFractionException e){
+ } catch (RuntimeException e){
e.printStackTrace();
return Double.toString(doubleValue);
}
@@ -175,97 +175,5 @@ public class FractionFormat extends Format {
public Object parseObject(String source, ParsePosition pos) {
throw new NotImplementedException("Reverse parsing not supported");
}
-
- private SimpleFraction calcFractionMaxDenom(double value, int maxDenominator)
- throws SimpleFractionException{
- /*
- * Lifted wholesale from org.apache.math.fraction.Fraction 2.2
- */
- double epsilon = 0.000000000001f;
- int maxIterations = 100;
- long overflow = Integer.MAX_VALUE;
- double r0 = value;
- long a0 = (long)Math.floor(r0);
- if (Math.abs(a0) > overflow) {
- throw new SimpleFractionException(
- String.format("value > Integer.MAX_VALUE: %d.", a0));
- }
-
- // check for (almost) integer arguments, which should not go
- // to iterations.
- if (Math.abs(a0 - value) < epsilon) {
- return new SimpleFraction((int) a0, 1);
- }
-
- long p0 = 1;
- long q0 = 0;
- long p1 = a0;
- long q1 = 1;
-
- long p2 = 0;
- long q2 = 1;
-
- int n = 0;
- boolean stop = false;
- do {
- ++n;
- double r1 = 1.0 / (r0 - a0);
- long a1 = (long)Math.floor(r1);
- p2 = (a1 * p1) + p0;
- q2 = (a1 * q1) + q0;
- if ((Math.abs(p2) > overflow) || (Math.abs(q2) > overflow)) {
- throw new SimpleFractionException(
- String.format("Greater than overflow in loop %f, %d, %d", value, p2, q2));
- }
-
- double convergent = (double)p2 / (double)q2;
- if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
- p0 = p1;
- p1 = p2;
- q0 = q1;
- q1 = q2;
- a0 = a1;
- r0 = r1;
- } else {
- stop = true;
- }
- } while (!stop);
-
- if (n >= maxIterations) {
- throw new SimpleFractionException("n greater than max iterations " + value + " : " + maxIterations);
- }
-
- if (q2 < maxDenominator) {
- return new SimpleFraction((int) p2, (int) q2);
- } else {
- return new SimpleFraction((int) p1, (int) q1);
- }
- }
-
- private SimpleFraction calcFractionExactDenom(double val, int exactDenom){
- int num = (int)Math.round(val*(double)exactDenom);
- return new SimpleFraction(num,exactDenom);
- }
-
- private class SimpleFraction {
- private final int num;
- private final int denom;
-
- public SimpleFraction(int num, int denom) {
- this.num = num;
- this.denom = denom;
- }
-
- public int getNumerator() {
- return num;
- }
- public int getDenominator() {
- return denom;
- }
- }
- private class SimpleFractionException extends Throwable{
- private SimpleFractionException(String message){
- super(message);
- }
- }
+
}
diff --git a/src/testcases/org/apache/poi/ss/format/TestCellFormat.java b/src/testcases/org/apache/poi/ss/format/TestCellFormat.java
index fdc57f4ead..42adda983b 100644
--- a/src/testcases/org/apache/poi/ss/format/TestCellFormat.java
+++ b/src/testcases/org/apache/poi/ss/format/TestCellFormat.java
@@ -825,4 +825,15 @@ public class TestCellFormat extends TestCase {
}
+ public void testSimpleFractionFormat() {
+ CellFormat cf1 = CellFormat.getInstance("# ?/?");
+ // Create a workbook, row and cell to test with
+ Workbook wb = new HSSFWorkbook();
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(0);
+ Cell cell = row.createCell(0);
+ cell.setCellValue(123456.6);
+ System.out.println(cf1.apply(cell).text);
+ assertEquals("123456 3/5", cf1.apply(cell).text);
+ }
} \ No newline at end of file