Division macht Probleme

NicoHatProbleme2

Bekanntes Mitglied
Ich habe letztens eine Taschenrechner-ähnliche Funktion geschaffen, die es mir erlaubt, einen String zu einem double zu machen.
Doch mir ist aufgefallen, dass bei der Division immer nicht-gewollte Kommastellen im e-2 Bereich erstellt werden.
Ich habe sogar mal BigDecimal benutzt, was aber irgendwie nichts gebracht hat... Nur die Division macht Probleme, der Rest macht alles gut. 8/2 = 4.02, 8*0.5 = 4.
Woran liegt das? (Ich habe sogar mal einfach statt
Java:
part = "" + new BigDecimal(c[j-1]).divide(new BigDecimal(c[j+1])).doubleValue();
Java:
part = "" + new BigDecimal(8).divide(new BigDecimal(8)).doubleValue();
ausprobiert, was auch nichts bringt.
Java:
package Tools;

import java.math.BigDecimal;

public class Functions {
   
    public static double returnResult(String s) {
        s = "(" + s + ")";
      
        char[] c = s.toCharArray();
        int[] brPos = new int[c.length];
        int bracket = 0;
        int brackets = 0;

        //get pos of brackets
        for(int i = 0; i < c.length; i++) {
            switch(c[i]) {
            case '(': brPos[i] = 2 + bracket+10; bracket+=10; brackets++; break;
            case ')': brPos[i] = 1 + bracket; bracket-=10; brackets++; break;
            default: brPos[i] = 0; break;
            }
        }
        //System.out.println();
        boolean parsesBr = true;
        String[] res = new String[brackets/2];
        String part = "";
        int amountOBrs = 0;
        for(int i = c.length-1; i >= 0; i--) {
            if(brPos[i] %10 == 2) {
                part = "";
                //String of brackets
                parsesBr = true;
                int br = brPos[i];
                for(int j = i+1; parsesBr; j++) {
                    //System.out.println(part);
                    if(brPos[j]+1 == br) parsesBr = false;
                    else part += c[j];
                }
                res[amountOBrs] = part;
                amountOBrs++;
            }
        }
      
        return Double.valueOf(calcBracket(res));
    }
    private static String calcBracket(String[] s) {
        String[] c;
        String part = "";
        String replace = "";
        String bracketsReplace = "";
        int check = 0;
        for(int i = 0; i < s.length; i++) {
            bracketsReplace = s[i];
            c = toStringArray(s[i]);
            //System.out.println("restart");
            //System.out.println();
            //Loop till everything is calculated
            Loop:
            while(true) {
                check = 0;
                c = toStringArray(s[i]);
                for(int j = 0; j < c.length; j++) {
                    switch(c[j]) {
                    case "^": {
                        part = "" + (Math.pow(Double.parseDouble("" + c[j-1]) , Double.parseDouble("" + c[j+1])));
                        replace = "" + c[j-1] + c[j] + c[j+1];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("^replace: " + replace + ", " + part + " " + s[i]);
                        check++;
                        break;
                      
                    }
                    case "%": {
                        part = "" + Double.parseDouble("" + c[j-1]) / 100;
                        replace = "" + c[j-1] + c[j];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("%replace: " + replace + " " + part + " " + s[i]);
                        check++;
                        break;
                    }
                    }
                }
                if(check == 0) break Loop;
            }
            //System.out.println();
            //Loop till everything is calculated
            Loop:
            while(true) {
                check = 0;
                c = toStringArray(s[i]);
                for(int j = 0; j < c.length; j++) {
                    switch(c[j]) {
                    case "*": {
                        part = "" + new BigDecimal(c[j-1]).multiply(new BigDecimal(c[j+1])).doubleValue();
                        replace = "" + c[j-1] + c[j] + c[j+1];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("*replace: " + replace + ", " + part + " '" + s[i] + "'");
                        check++;
                        break;
                    }
                    case "/": {
                        //Hier ist wohl irgendwas falsch
                        part = "" + new BigDecimal(c[j-1]).divide(new BigDecimal(c[j+1])).doubleValue();
                        replace = "" + c[j-1] + c[j];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("/replace: " + replace + " " + part + " " + s[i]);
                        check++;
                        break;
                    }
                    }
                }
                if(check == 0) break Loop;
            }
            //Loop till everything is calculated
            Loop:
            while(true) {
                check = 0;
                c = toStringArray(s[i]);
                for(int j = 0; j < c.length; j++) {
                    switch(c[j]) {
                    case "+": {
                        part = "" + (Double.parseDouble("" + c[j-1]) + Double.parseDouble("" + c[j+1]));
                        replace = "" + c[j-1] + c[j] + c[j+1];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("+replace: " + replace + ", " + part + " '" + s[i] + "'");
                        check++;
                        break;
                        }
                    case "-": {
                        part = "" + (Double.parseDouble("" + c[j-1]) - Double.parseDouble("" + c[j+1]));
                        replace = "" + c[j-1] + c[j] +c[j+1];
                        s[i] = s[i].replace(replace, part);
                        //System.out.println("-replace: " + replace + " " + part + " " + s[i]);
                        check++;
                        break;
                    }
                    }
                }
                if(check == 0) break Loop;
            }
            //replace the String in the other Brackets
            for(int j = 0; j < s.length; j++) {
                if(j != i) {
                    //System.out.println("BrReplace: " + s[j] + ", " + bracketsReplace + ", " + s[i]);
                    s[j] = s[j].replace("(" + bracketsReplace + ")", s[i]);
                    //System.out.println(s[j]);
                }
            }
        }
        //System.out.println(s[s.length-1]);
        return s[s.length-1];
    }
    public static String[] toStringArray(String s) {
        //System.out.println(s);
        char[] c = s.toCharArray();
        String[] ar = new String[c.length];
        int j = 0;
        int k = 0;
        for(int i = 0; j < c.length; j++) {
            //System.out.println(j + ":" + i);
            if(i < c.length) ar[i] = "";
            while(j < c.length && (Character.isDigit(c[j]) | c[j] == '.')) {
                ar[i] += c[j];
                j++;
                //System.out.println(ar[i]);
              
            }
            i++;
            if(i < c.length) ar[i] = "";
            if(j < c.length && !Character.isDigit(c[j]) /**&& c[i] != '(' && c[i] != ')'**/) {
                ar[i] += c[j];
                //System.out.println(ar[i]);
                i++;
            }
            k = i;
            //System.out.println(i);
          
        }
        String[] res = new String[k];
        for(int i = 0; i < k; i++) {
            res[i] = ar[i];
            //System.out.println("a:" + ar[i]);
        }
        return res;
    }

}
 
Zuletzt bearbeitet:
Also keine Fließkommaberechnung der Welt macht aus 8/4 = 4.02.
Kann es sein, dass deine ganze Stringkonkatenation in deinem "Parser" (in Anführungszeichen) zuerst korrekt 8/2 = 4.0 ausrechnet und du das dann stringkonkatenierst mit einer 2 irgendwo danach?
Ich würde deinen komplett "Parser" wegschmeißen und nochmal neu bauen. Da steigt man ja komplett überhaupt nicht durch.
Schau dir mal bitte das Thema "recusive descent parser" an. Ein arithmetischer Ausdruck kann sehr einfach rekursiv geparsed werden.
 
Sehr schön. Dann kannst du das ja jetzt alles in die Tonne treten und nochmal vernünftig von Vorne anfangen. Ohne komische Arrays und Schleifen und ohne String-Replacements. 🙂
 
Also wenn du zum Beispiel "8/8" zerlegen willst, dann schlage ich zwei Schritte vor: Zuerst zerlegst du den Ausdruck in einzelne Teile (Tokens), dann hast du drei Teile "8", "/" und "8". Aus dieser Liste an einzelnen Teilen kannst du dann deine Rechnung aufbauen.
 
hab ich ja sozusagen... habe halt nur ein Array gemacht, um so was, wie 228.76 einfacher aufzurufen. Auch habe ich Klammern hinzugefügt, was alles etwas schwieriger macht
 
Ich empfehle dir nochmals, den Parser rekursiv aufzubauen. Hier mal ein Beispiel mit korrekter Klammerung und Bindungen der Operatoren, was ich eben mal from scratch gebaut hab:
Java:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Parser {
  private static final Pattern number = Pattern.compile("^-?\\d+(\\.\\d+)?"); // <- to recognized numbers more easily
  private final String s;
  private int pos;
  public Parser(String input) {
    this.s = input;
  }
  private void advance(int n) {
    pos += n;
  }
  private void ws() {
    while (pos < s.length() && Character.isWhitespace(s.charAt(pos)))
      advance(1);
  }
  private double number() {
    Matcher m = number.matcher(s.substring(pos));
    if (m.find()) {
      advance(m.end());
      return Double.parseDouble(m.group());
    } else {
      throw new IllegalStateException("expecting number at " + pos);
    }
  }
  private double primitive() {
    ws();
    char c = s.charAt(pos);
    if (c == '(') {
      advance(1); // <- consume open parenthesis
      double v = expression();
      advance(1); // <- consume close parenthesis
      return v;
    }
    return number();
  }
  private double additive() {
    double ret = multiplicative();
    ws();
    while (pos < s.length()) {
      char c = s.charAt(pos);
      if (c == '+') {
        advance(1); // <- consume the + character
        ret += multiplicative();
      } else if (c == '-') {
        advance(1); // <- consume the - character
        ret -= multiplicative();
      } else {
        break;
      }
      ws();
    }
    return ret;
  }
  private double multiplicative() {
    double ret = primitive();
    ws();
    while (pos < s.length()) {
      char c = s.charAt(pos);
      if (c == '*') {
        advance(1); // <- consume the * character
        ret *= primitive();
      } else if (c == '/') {
        advance(1); // <- consume the / character
        ret /= primitive();
      } else {
        break;
      }
      ws();
    }
    return ret;
  }
  public double expression() {
    return additive();
  }

  // Test
  public static void main(String[] args) {
    System.out.println(new Parser(" ( (  4 +  2 ) - 3 ) + 2 * -2 ").expression());
  }
}
 
Hier ist was mit ^ und % und parsed äquivalent zu Google.
Z.B. ist " 5 * 100% + - 3 ^ 2% * (-2.5 + 3 ^ - 2*4 )" == 7.10122..
Java:
import java.util.function.BiFunction;
import java.util.function.DoubleSupplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Parser {
  private static final Pattern number = Pattern.compile("^\\d+(\\.\\d+)?"); // <- to recognize numbers more easily
  private final String s;
  private int pos;
  public Parser(String input) {
    this.s = input;
  }
  private void ws() {
    while (pos < s.length() && Character.isWhitespace(s.charAt(pos)))
      pos += 1;
  }
  private double number() {
    Matcher m = number.matcher(s.substring(pos));
    if (m.find())
      return consumeAnd(m.end(), () -> Double.parseDouble(m.group()));
    throw new IllegalStateException("expecting number at " + pos);
  }
  private double primary() {
    ws();
    if (s.charAt(pos) == '(') {
      double v = consumeAnd(1, this::expression);
      pos += 1; // <- consume close parenthesis
      return v;
    }
    return number();
  }
  private double exponential() {
    double ret = primary();
    ws();
    if (pos < s.length() && s.charAt(pos) == '^')
      return consumeAnd(1, () -> Math.pow(ret, unary()));
    return ret;
  }
  private double unary() {
    ws();
    if (s.charAt(pos) == '-')
      return -consumeAnd(1, this::unary);
    return percent(exponential());
  }
  private double percent(double v) {
    return fold(v, (acc, c) -> {
      if (c == '%')
        return consumeAnd(1, () -> acc / 100.0);
      return null;
    });
  }
  private double consumeAnd(int n, DoubleSupplier f) {
    pos += n;
    return f.getAsDouble();
  }
  private double fold(double ret, BiFunction<Double, Character, Double> f) {
    ws();
    while (pos < s.length()) {
      Double d = f.apply(ret, s.charAt(pos));
      if (d == null)
        break;
      ret = d;
      ws();
    }
    return ret;
  }
  private double multiplicative() {
    return fold(unary(), (acc, c) -> switch (c) {
      case '*' -> acc * consumeAnd(1, this::unary);
      case '/' -> acc / consumeAnd(1, this::unary);
      default -> null;
    });
  }
  private double additive() {
    return fold(multiplicative(), (acc, c) -> switch (c) {
      case '+' -> acc + consumeAnd(1, this::multiplicative);
      case '-' -> acc - consumeAnd(1, this::multiplicative);
      default -> null;
    });
  }
  public double expression() {
    return additive();
  }

  // Test
  public static void main(String[] args) {
    System.out.println(new Parser(" 5 * 100% + - 3 ^ 2% * (-2.5 + 3 ^ - 2*4 ) ").expression());
  }
}
 
Zuletzt bearbeitet:

Zurück
Oben