r(m, "MULTINOMIAL", null);
r(m, "NETWORKDAYS", NetworkdaysFunction.instance);
r(m, "NOMINAL", null);
- r(m, "OCT2BIN", null);
+ r(m, "OCT2BIN", Oct2Dec.instance);
r(m, "OCT2DEC", null);
r(m, "OCT2HEX", null);
r(m, "ODDFPRICE", null);
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+/**\r
+ * <p>Some utils for converting from and to any base<p/>\r
+ *\r
+ * @author cedric dot walter @ gmail dot com\r
+ */\r
+public class BaseNumberUtils {\r
+\r
+\r
+ public static double convertToDecimal(String value, int base, int maxNumberOfPlaces) throws IllegalArgumentException {\r
+ if (value.isEmpty()) {\r
+ return 0.0;\r
+ }\r
+\r
+ long stringLength = value.length();\r
+ if (stringLength > maxNumberOfPlaces) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+\r
+ double decimalValue = 0.0;\r
+\r
+ long signedDigit = 0;\r
+ boolean hasSignedDigit = true;\r
+ char[] characters = value.toCharArray();\r
+ for (char character : characters) {\r
+ long digit;\r
+\r
+ if ('0' <= character && character <= '9') {\r
+ digit = character - '0';\r
+ } else if ('A' <= character && character <= 'Z') {\r
+ digit = 10 + (character - 'A');\r
+ } else if ('a' <= character && character <= 'z') {\r
+ digit = 10 + (character - 'a');\r
+ } else {\r
+ digit = base;\r
+ }\r
+\r
+ if (digit < base) {\r
+ if (hasSignedDigit) {\r
+ hasSignedDigit = false;\r
+ signedDigit = digit;\r
+ }\r
+ decimalValue = decimalValue * base + digit;\r
+ } else {\r
+ throw new IllegalArgumentException("character not allowed");\r
+ }\r
+ }\r
+\r
+ boolean isNegative = (!hasSignedDigit && stringLength == maxNumberOfPlaces && (signedDigit >= base / 2));\r
+ if (isNegative) {\r
+ decimalValue = getTwoComplement(base, maxNumberOfPlaces, decimalValue);\r
+ decimalValue = decimalValue * -1.0;\r
+ }\r
+\r
+ return decimalValue;\r
+ }\r
+\r
+ private static double getTwoComplement(double base, double maxNumberOfPlaces, double decimalValue) {\r
+ return (Math.pow(base, maxNumberOfPlaces) - decimalValue);\r
+ }\r
+}\r
import org.apache.poi.ss.formula.OperationEvaluationContext;\r
import org.apache.poi.ss.formula.eval.*;\r
\r
-import java.math.BigInteger;\r
-\r
/**\r
* Implementation for Excel HEX2DEC() function.<p/>\r
* <p/>\r
\r
public static final FreeRefFunction instance = new Hex2Dec();\r
\r
+ static final int HEXADECIMAL_BASE = 16;\r
+ static final int MAX_NUMBER_OF_PLACES = 10;\r
+\r
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE) {\r
- String number = OperandResolver.coerceValueToString(numberVE);\r
- if (number.length() > 10) {\r
+ String hex = OperandResolver.coerceValueToString(numberVE);\r
+ try {\r
+ return new NumberEval(BaseNumberUtils.convertToDecimal(hex, HEXADECIMAL_BASE, MAX_NUMBER_OF_PLACES));\r
+ } catch (IllegalArgumentException e) {\r
return ErrorEval.NUM_ERROR;\r
}\r
-\r
- String unsigned;\r
- boolean isPositive = false;\r
- boolean isNegative = false;\r
- if (number.length() < 10) {\r
- unsigned = number;\r
- isPositive = true;\r
- } else {\r
- //remove sign bit\r
- unsigned = number.substring(1);\r
- isNegative =\r
- number.startsWith("8") || number.startsWith("9") ||\r
- number.startsWith("A") || number.startsWith("B") ||\r
- number.startsWith("C") || number.startsWith("D") ||\r
- number.startsWith("E") || number.startsWith("F");\r
- }\r
-\r
- long decimal;\r
- if (isPositive) {\r
- try {\r
- decimal = Integer.parseInt(unsigned, 16);\r
- } catch (NumberFormatException ee) {\r
- // number is not a valid hexadecimal number\r
- return ErrorEval.NUM_ERROR;\r
- }\r
- } else {\r
- if (isNegative) {\r
- BigInteger temp = new BigInteger(unsigned, 16);\r
- BigInteger subtract = BigInteger.ONE.shiftLeft(unsigned.length() * 4);\r
- temp = temp.subtract(subtract);\r
- decimal = temp.longValue();\r
- } else {\r
- try {\r
- decimal = Integer.parseInt(unsigned, 16);\r
- } catch (NumberFormatException ee) {\r
- // number is not a valid hexadecimal number\r
- return ErrorEval.NUM_ERROR;\r
- }\r
- }\r
- }\r
-\r
- return new NumberEval(decimal);\r
}\r
\r
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {\r
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import org.apache.poi.ss.formula.OperationEvaluationContext;\r
+import org.apache.poi.ss.formula.eval.ErrorEval;\r
+import org.apache.poi.ss.formula.eval.NumberEval;\r
+import org.apache.poi.ss.formula.eval.OperandResolver;\r
+import org.apache.poi.ss.formula.eval.ValueEval;\r
+\r
+/**\r
+ * <p>Implementation for Excel Oct2Dec() function.<p/>\r
+ * <p>\r
+ * Converts an octal number to decimal.\r
+ * </p>\r
+ * <p>\r
+ * <b>Syntax</b>:<br/> <b>Oct2Dec </b>(<b>number</b> )\r
+ * </p>\r
+ * <p/>\r
+ * Number is the octal number you want to convert. Number may not contain more than 10 octal characters (30 bits).\r
+ * The most significant bit of number is the sign bit. The remaining 29 bits are magnitude bits.\r
+ * Negative numbers are represented using two's-complement notation..\r
+ * <p/>\r
+ * If number is not a valid octal number, OCT2DEC returns the #NUM! error value.\r
+ *\r
+ * @author cedric dot walter @ gmail dot com\r
+ */\r
+public class Oct2Dec extends Fixed1ArgFunction implements FreeRefFunction {\r
+\r
+ public static final FreeRefFunction instance = new Oct2Dec();\r
+\r
+ static final int MAX_NUMBER_OF_PLACES = 10;\r
+ static final int OCTAL_BASE = 8;\r
+\r
+ public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE) {\r
+ String octal = OperandResolver.coerceValueToString(numberVE);\r
+ try {\r
+ return new NumberEval(BaseNumberUtils.convertToDecimal(octal, OCTAL_BASE, MAX_NUMBER_OF_PLACES));\r
+ } catch (IllegalArgumentException e) {\r
+ return ErrorEval.NUM_ERROR;\r
+ }\r
+ }\r
+\r
+ public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {\r
+ if (args.length != 1) {\r
+ return ErrorEval.VALUE_INVALID;\r
+ }\r
+ return evaluate(ec.getRowIndex(), ec.getColumnIndex(), args[0]);\r
+ }\r
+}\r
--- /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.formula.functions;
+
+import junit.framework.TestCase;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.StringEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+
+/**
+ * Tests for {@link Hex2Dec}
+ *
+ * @author cedric dot walter @ gmail dot com
+ */
+public final class TestHex2Dec extends TestCase {
+
+ private static ValueEval invokeValue(String number1) {
+ ValueEval[] args = new ValueEval[] { new StringEval(number1) };
+ return new Hex2Dec().evaluate(args, -1, -1);
+ }
+
+ private static void confirmValue(String msg, String number1, String expected) {
+ ValueEval result = invokeValue(number1);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(msg, expected, ((NumberEval) result).getStringValue());
+ }
+
+ private static void confirmValueError(String msg, String number1, ErrorEval numError) {
+ ValueEval result = invokeValue(number1);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(msg, numError, result);
+ }
+
+ public void testBasic() {
+ confirmValue("Converts octal 'A5' to decimal (165)", "A5", "165");
+ confirmValue("Converts octal FFFFFFFF5B to decimal (-165)", "FFFFFFFF5B", "-165");
+ confirmValue("Converts octal 3DA408B9 to decimal (-165)", "3DA408B9", "1034160313");
+ }
+
+ public void testErrors() {
+ confirmValueError("not a valid octal number","GGGGGGG", ErrorEval.NUM_ERROR);
+ confirmValueError("not a valid octal number","3.14159", ErrorEval.NUM_ERROR);
+ }
+}
--- /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.formula.functions;
+
+import junit.framework.TestCase;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.StringEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+
+/**
+ * Tests for {@link org.apache.poi.ss.formula.functions.Oct2Dec}
+ *
+ * @author cedric dot walter @ gmail dot com
+ */
+public final class TestOct2Dec extends TestCase {
+
+ private static ValueEval invokeValue(String number1) {
+ ValueEval[] args = new ValueEval[] { new StringEval(number1) };
+ return new Oct2Dec().evaluate(args, -1, -1);
+ }
+
+ private static void confirmValue(String msg, String number1, String expected) {
+ ValueEval result = invokeValue(number1);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(msg, expected, ((NumberEval) result).getStringValue());
+ }
+
+ private static void confirmValueError(String msg, String number1, ErrorEval numError) {
+ ValueEval result = invokeValue(number1);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(msg, numError, result);
+ }
+
+ public void testBasic() {
+ confirmValue("Converts octal '' to decimal (0)", "", "0");
+ confirmValue("Converts octal 54 to decimal (44)", "54", "44");
+ confirmValue("Converts octal 7777777533 to decimal (-165)", "7777777533", "-165");
+ confirmValue("Converts octal 7000000000 to decimal (-134217728)", "7000000000", "-134217728");
+ confirmValue("Converts octal 7776667533 to decimal (-299173)", "7776667533", "-299173");
+ }
+
+ public void testErrors() {
+ confirmValueError("not a valid octal number","ABCDEFGH", ErrorEval.NUM_ERROR);
+ confirmValueError("not a valid octal number","99999999", ErrorEval.NUM_ERROR);
+ confirmValueError("not a valid octal number","3.14159", ErrorEval.NUM_ERROR);
+ }
+}