精度の高い計算をJavaで行うにはBigDecimalを使用するのが適当と聞き
計算式をformulaに代入し計算処理するプログラムを記述したのですが、1.1*3の結果が3.3000000000000003となり丸め誤差が修正されませんでした。
計算式内の個々の数値と演算子を格納する変数を用意して計算させようとも思ったのですが、複雑になりそうなので止めました。
以下のプログラムを大幅に変更することなく丸め誤差が修正できるよう修正したいのですが、良い方法はございませんでしょうか。
java
1//計算式を格納する 2String formula=""; 3 4try{ 5 ScriptEngineManager factory=new ScriptEngineManager(); 6 ScriptEngine engine=factory.getEngineByName("JavaScript"); 7 BigDecimal result=new BigDecimal( (engine.eval(formula)).toString() ).stripTrailingZeros(); 8 jTextField1.setText(result.toPlainString()); 9}catch(ScriptException ex){ 10 throw new RuntimeException(ex); 11}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
JavaScriptとして実行させていますので、engine.eval(formula)
の時点ですでに誤差を含んだ数値となってしまっています(もっと言えば、1.1
をJavaScriptで表現した時点で、もはや正確でなくなります)。それをBigDecimal
に入れても手遅れです。
JavaScriptとして処理するという手抜きではなく、自前で数式を処理する必要があります。
投稿2016/10/27 03:00
総合スコア145183
0
ベストアンサー
Stringの数式を構文解析する、という方法もあります(これまた自力で実装しようとすると大変ですが)。
参考となるコードが
http://stackoverflow.com/questions/3422673/evaluating-a-math-expression-given-in-string-form
にありましたので BigDecimal で書き直してみました。
BigDecimal なので sqrt 等の関数が使えないのでコメントアウトしています。
assertEquals("1.3",Expression.eval("13/10").toString()); assertEquals("1.30",Expression.eval("0.13*10").toString()); assertEquals("1.23",Expression.eval("0.13+1.10").toString()); assertEquals("0.03",Expression.eval("0.13-0.10").toString()); assertEquals("0.03",Expression.eval("-0.10+0.13").toString()); assertEquals("0.03",Expression.eval("+0.13-0.10").toString()); assertEquals("0.03",Expression.eval("+0.13-(0.05+0.05)").toString());
等の動作確認済み。
public static BigDecimal eval(final String str) { return new Object() { int pos = -1, ch; void nextChar() { ch = (++pos < str.length()) ? str.charAt(pos) : -1; } boolean eat(int charToEat) { while (ch == ' ') nextChar(); if (ch == charToEat) { nextChar(); return true; } return false; } BigDecimal parse() { nextChar(); BigDecimal x = parseExpression(); if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch); return x; } // Grammar: // expression = term | expression `+` term | expression `-` term // term = factor | term `*` factor | term `/` factor // factor = `+` factor | `-` factor | `(` expression `)` // | number | functionName factor | factor `^` factor BigDecimal parseExpression() { BigDecimal x = parseTerm(); for (;;) { if (eat('+')) x = x.add(parseTerm()); // addition else if (eat('-')) x = x.subtract(parseTerm()); // subtraction else return x; } } BigDecimal parseTerm() { BigDecimal x = parseFactor(); for (;;) { if (eat('*')) x = x.multiply(parseFactor()); // multiplication else if (eat('/')) x = x.divide(parseFactor()); // division else return x; } } BigDecimal parseFactor() { if (eat('+')) return parseFactor(); // unary plus if (eat('-')) return parseFactor().negate(); // unary minus BigDecimal x; int startPos = this.pos; if (eat('(')) { // parentheses x = parseExpression(); eat(')'); } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers while ((ch >= '0' && ch <= '9') || ch == '.') nextChar(); x = new BigDecimal(str.substring(startPos, this.pos)); } else if (ch >= 'a' && ch <= 'z') { // functions while (ch >= 'a' && ch <= 'z') nextChar(); String func = str.substring(startPos, this.pos); x = parseFactor(); /* if (func.equals("sqrt")) x = Math.sqrt(x); else if (func.equals("sin")) x = Math.sin(Math.toRadians(x)); else if (func.equals("cos")) x = Math.cos(Math.toRadians(x)); else if (func.equals("tan")) x = Math.tan(Math.toRadians(x)); else */ throw new RuntimeException("Unknown function: " + func); } else { throw new RuntimeException("Unexpected: " + (char)ch); } // if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation return x; } }.parse(); }
投稿2016/10/27 04:36
総合スコア46
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/10/27 03:05
2016/10/27 03:59