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();
}
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;
- }
-
- }
}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+
+}
+
import java.util.regex.Matcher;\r
import java.util.regex.Pattern;\r
\r
+import org.apache.poi.ss.format.SimpleFraction;\r
import org.apache.poi.ss.formula.eval.NotImplementedException;\r
\r
/**\r
* If further uses for Commons Math are found, we will consider adding it as a dependency.\r
* For now, we have in-lined the one method to keep things simple.</p>\r
*/\r
-/* One question remains...is the value of epsilon in calcFractionMaxDenom reasonable? */\r
+\r
@SuppressWarnings("serial")\r
public class FractionFormat extends Format {\r
private final static Pattern DENOM_FORMAT_PATTERN = Pattern.compile("(?:(#+)|(\\d+))");\r
}\r
\r
//this is necessary to prevent overflow in the maxDenom calculation\r
- //stink1\r
if (wholePart+(int)decPart == wholePart+decPart){\r
\r
StringBuilder sb = new StringBuilder();\r
try{\r
//this should be the case because of the constructor\r
if (exactDenom > 0){\r
- fract = calcFractionExactDenom(decPart, exactDenom);\r
+ fract = SimpleFraction.buildFractionExactDenominator(decPart, exactDenom);\r
} else {\r
- fract = calcFractionMaxDenom(decPart, maxDenom);\r
+ fract = SimpleFraction.buildFractionMaxDenominator((double)decPart, maxDenom);\r
}\r
- } catch (SimpleFractionException e){\r
+ } catch (RuntimeException e){\r
e.printStackTrace();\r
return Double.toString(doubleValue);\r
}\r
public Object parseObject(String source, ParsePosition pos) {\r
throw new NotImplementedException("Reverse parsing not supported");\r
}\r
-\r
- private SimpleFraction calcFractionMaxDenom(double value, int maxDenominator) \r
- throws SimpleFractionException{\r
- /*\r
- * Lifted wholesale from org.apache.math.fraction.Fraction 2.2\r
- */\r
- double epsilon = 0.000000000001f;\r
- int maxIterations = 100;\r
- long overflow = Integer.MAX_VALUE;\r
- double r0 = value;\r
- long a0 = (long)Math.floor(r0);\r
- if (Math.abs(a0) > overflow) {\r
- throw new SimpleFractionException(\r
- String.format("value > Integer.MAX_VALUE: %d.", a0));\r
- }\r
-\r
- // check for (almost) integer arguments, which should not go\r
- // to iterations.\r
- if (Math.abs(a0 - value) < epsilon) {\r
- return new SimpleFraction((int) a0, 1);\r
- }\r
-\r
- long p0 = 1;\r
- long q0 = 0;\r
- long p1 = a0;\r
- long q1 = 1;\r
-\r
- long p2 = 0;\r
- long q2 = 1;\r
-\r
- int n = 0;\r
- boolean stop = false;\r
- do {\r
- ++n;\r
- double r1 = 1.0 / (r0 - a0);\r
- long a1 = (long)Math.floor(r1);\r
- p2 = (a1 * p1) + p0;\r
- q2 = (a1 * q1) + q0;\r
- if ((Math.abs(p2) > overflow) || (Math.abs(q2) > overflow)) {\r
- throw new SimpleFractionException(\r
- String.format("Greater than overflow in loop %f, %d, %d", value, p2, q2));\r
- }\r
-\r
- double convergent = (double)p2 / (double)q2;\r
- if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {\r
- p0 = p1;\r
- p1 = p2;\r
- q0 = q1;\r
- q1 = q2;\r
- a0 = a1;\r
- r0 = r1;\r
- } else {\r
- stop = true;\r
- }\r
- } while (!stop);\r
-\r
- if (n >= maxIterations) {\r
- throw new SimpleFractionException("n greater than max iterations " + value + " : " + maxIterations);\r
- }\r
-\r
- if (q2 < maxDenominator) {\r
- return new SimpleFraction((int) p2, (int) q2);\r
- } else {\r
- return new SimpleFraction((int) p1, (int) q1);\r
- }\r
- }\r
-\r
- private SimpleFraction calcFractionExactDenom(double val, int exactDenom){\r
- int num = (int)Math.round(val*(double)exactDenom);\r
- return new SimpleFraction(num,exactDenom);\r
- }\r
-\r
- private class SimpleFraction {\r
- private final int num;\r
- private final int denom;\r
-\r
- public SimpleFraction(int num, int denom) {\r
- this.num = num;\r
- this.denom = denom;\r
- }\r
-\r
- public int getNumerator() {\r
- return num;\r
- }\r
- public int getDenominator() {\r
- return denom;\r
- }\r
- }\r
- private class SimpleFractionException extends Throwable{\r
- private SimpleFractionException(String message){\r
- super(message);\r
- }\r
- }\r
+ \r
}\r
}
+ 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