1

In some unsuccessful attempts to create a calculator in java that follows the BODMAS rule to solve complex mathematical expressions, I decided that it was important I create a method whose argument is a string. Such a method should detect braces and mathematical signs(i.e plus, times, minus, divide, open and close brackets) and should convert any string say temp that is between any two of the signs above into double say temp2 unless temp is empty(i.e ""). The method then stores temp2 values and the signs in a list of Objects in the same order as in the argument. The method takes into consideration that if the first char is a number, then the first object to be placed in the list should be the double value of a substring of the argument from zero to the index of the first sign. The equivalent is done if the last char is a number at the last index of the list. The above idea led me to create the project below:

package maths.calculator;

import java.util.ArrayList;

public class NumGetter {
    static String expression;
    static ArrayList<Integer> preList = new ArrayList<>();
    static ArrayList<Object> list = new ArrayList<>();

    public static ArrayList<Object> getNumber(String num) {
        expression = num;
        try {
            if (expression == null) {
                expression = "";
            }
            if (expression == "") {
                return list;
            }

            int condition = 0;
            for (char chars : expression.toCharArray()) {
                if (chars == '-' || chars == '+' || chars == '÷' || chars == '×' || chars == ')' || chars == '(') {
                    preList.add(condition);
                }
                condition++;
            }

            if (preList.size() != 0) {
                System.out.println(preList);
                if (expression.charAt(0) >= '0' && expression.charAt(0) <= '9') {
                    String local = expression.substring(0, preList.get(0));
                    list.add(Double.parseDouble(local));
                }

                for (int i = 0; i < (preList.size() - 1); i++) {
                    int min = preList.get(i);
                    int next = preList.get(i + 1);
                    String local = expression.substring(min + 1, next);
                    if (local.length() == 0) {
                        list.add(expression.charAt(min));
                    } else {
                        double temp = Double.parseDouble(local);
                        list.add(expression.charAt(min));
                        list.add(temp);
                    }
                }
                if (expression.charAt(num.length() - 1) >= '0' && expression.charAt(num.length() - 1) <= '9') {
                    condition = preList.get(preList.size() - 1);
                    String local = expression.substring(condition);
                    list.add(expression.charAt(condition));
                    list.add(Double.parseDouble(local));
                }

            } else {
                list.add(Double.parseDouble(expression));
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return list;
    }

}

The code above looked satisfactory enough even after I imported the class above and called the method getNumber as shown below:

import java.util.ArrayList;

import maths.calculator.NumGetter;

public class Text {
    public static void main(String[] args) {

        System.out.println("(12+8)0");
        String one = "(12+8)0";
        ArrayList<Object> two = NumGetter.getNumber(one);
        System.out.println(two.toString());

    }
}

The problem lies in the output. In every case the Exception is thrown. Typically for the above, the out put was:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
    at java.base/java.util.Objects.checkIndex(Objects.java:373)
    at java.base/java.util.ArrayList.get(ArrayList.java:425)
    at maths.calculator.NumGetter.getNumber(NumGetter.java:43)
    at demo.Text.main(Text.java:12)
(12+8)0
[0, 3, 5]

However when I change the iteration for (int i = 0; i < (preList.size() - 1); i++) to for (int i = 0; i < (preList.size() - 2); i++), which by the way, should be incorrect, the result was quite close to the expected answer:

    at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
    at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.base/java.lang.Double.parseDouble(Double.java:549)
    at maths.calculator.NumGetter.getNumber(NumGetter.java:51)
    at demo.Text.main(Text.java:12)
(12+8)0
[0, 3, 5]
[(, 12.0, )]

What possibly have gone wrong please... I very much need your good brains.

Isaac
  • 23
  • 5
  • 3
    Suggestions. (1) Use `e.printStackTrace();` instead of `System.out.println(e.getStackTrace());` - that's what it's for. (2) Step through your code with a debugger, if the exception stack trace doesn't make it clear what's happening. – Dawood ibn Kareem Nov 28 '19 at 04:34
  • *FYI:* `(chars < '0' || chars > '9' || chars != '.')` is always true. It *cannot* be false. That means `preList` will be a list of every index into `num`. Since `preList` will *never* have a `-1`, it also means that `list` will be the same. `number == ""` doesn't work (see [How do I compare strings in Java?](https://stackoverflow.com/q/513832/5221149)). Which means that the first 20 lines of the method are all broken / useless. – Andreas Nov 28 '19 at 04:59
  • All Java objects have a `toString()` method, so please use `two.toString()` to print its content. – LHCHIN Nov 28 '19 at 05:02
  • @LHCHIN `println(two)` and `println(two.toString())` is exactly the same, since `println(Object)` is implemented to call `toString()` for you. – Andreas Nov 28 '19 at 05:04
  • Remove the `try-catch` code. It's wrong, and it's hiding the error. Just get rid of it. – Andreas Nov 28 '19 at 05:07
  • `if (number == "") {` doesn't look right to me. Try `.equals()`. – Dawood ibn Kareem Nov 28 '19 at 05:09
  • @Andreas Thanks, you are right! I misread the line of output message. @Isaac I think `number.substring(++min, next)` may cause `StringIndexOutOfBoundException`! – LHCHIN Nov 28 '19 at 05:14
  • @Andreas `(chars < '0' || chars > '9' || chars != '.')` becomes false if chars is a numerical character – Isaac Nov 28 '19 at 05:27
  • @LHCHIN You're right. It was a StringIndexOutOfBoundsException. – Isaac Nov 28 '19 at 05:33
  • @Isaac Really?!? Let `chars = '3'`, then we have `chars < '0'` *(false)*, `chars > '9' *(false)*, and `chars != '.'` *(true)*, then OR them together and you have ... **true**. --- The only time `chars != '.'` will be false is when `chars = '.'`, and then one of the other two will be true. I don't even need to look as an ASCII table to find out which one, because it's guaranteed that one of them will be true, which means that the entire expression is **always true**. – Andreas Nov 28 '19 at 17:34
  • @Andreas You're right. – Isaac Nov 28 '19 at 20:20
  • @Andreas I seriously mistook. I think the right condition should be `if (chars < '0' || chars > '9' && chars != '.' )` so that it wouldn't be true for numerical characters and dots right? – Isaac Nov 28 '19 at 20:42
  • @Isaac Do you know the operator precedence rules of `||` and `&&`? Most people get them wrong, or are at the very least unsure, so it's always best to use `()` when mixing `||` and `&&` in an expression. *(hint: your code is wrong)* – Andreas Nov 28 '19 at 20:49
  • Poor title. Edit to summarize your specific technical issue. – Basil Bourque Nov 30 '19 at 06:52

2 Answers2

0

Following is the line throwing the exception java.lang.StringIndexOutOfBoundsException: String index out of range: -1 in the second iteration of the for loop:

String local = number.substring(++min, next); //line throwing exception

1st first iteration: number.substring(1, 1);

2nd iteration: number.substring(2, 1);

in the second iteration, it's throwing an exception because it's not allowed for the beginIndex of substring method to be greater than lastIndex. For more information please have a look here.

Thanks

Zain Ul Abideen
  • 1,617
  • 1
  • 11
  • 25
0

At long last. I can't believe the last bug was in the last if block:

String local = expression.substring(condition);

I reckon it is supposed to be:

String local = expression.substring(condition+1);

Thanks to everyone especially @Andreas

Isaac
  • 23
  • 5