]> source.dussan.org Git - poi.git/commitdiff
55419 and refactor SimpleFraction
authorTim Allison <tallison@apache.org>
Thu, 15 Aug 2013 01:46:25 +0000 (01:46 +0000)
committerTim Allison <tallison@apache.org>
Thu, 15 Aug 2013 01:46:25 +0000 (01:46 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1514123 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ss/format/CellNumberFormatter.java
src/java/org/apache/poi/ss/format/SimpleFraction.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/FractionFormat.java
src/testcases/org/apache/poi/ss/format/TestCellFormat.java

index 91f0979a0b999a94d3fd16413b03c039c0076259..d2b9100556079b1f5817b033adb9dc56c81d0b01 100644 (file)
@@ -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 (file)
index 0000000..1452b52
--- /dev/null
@@ -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;
+    }
+
+}
+
index 1522e7afb8460729b08b78d51fbb584cf6e44465..43564c885499dd183476236b9b7031e9c8d3b4a5 100644 (file)
@@ -22,6 +22,7 @@ import java.text.ParsePosition;
 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
@@ -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.\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
@@ -113,7 +114,6 @@ public class FractionFormat extends Format {
         }\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
@@ -128,11 +128,11 @@ public class FractionFormat extends Format {
         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
@@ -175,97 +175,5 @@ public class FractionFormat extends Format {
     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
index fdc57f4ead4d5f4bd44ddda1e2573da7746d7c11..42adda983b3d1ba6c9ae5151f513b61681415207 100644 (file)
@@ -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