2017-05-29 44 views
1

我想製作一個採用字符串公式的方法,並通過以非常小的間隔執行黎曼和來解決該公式的積分。我正在使用ScriptEngine和ScriptEngineManager類來評估函數(使用eval()方法)。出於某種原因,我得到這個錯誤:爲什麼eval類會給我一個從int到double的轉換錯誤?

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double at sum.integral(sum.java:31) at sum.main(sum.java:13)

import java.beans.Expression; 

import javax.script.ScriptEngine; 
import javax.script.ScriptEngineManager; 
import javax.script.ScriptException; 

public class sum { 

    //testing method 
    public static void main(String[] args) throws ScriptException { 

     double x = integral("5*x^2",0,5); 
     System.out.println(x); 

    } 

    public static double integral(String function, double lower, double upper) throws ScriptException 
    { 
     double total = 0; 

     ScriptEngineManager mgr = new ScriptEngineManager(); 
     ScriptEngine engine = mgr.getEngineByName("JavaScript"); 

     //Solves function from upper to lower with a .001 interval, adding it to the total. 
     for (double i = lower; i < upper; i+=.001) 
     { 
      //evaluates the interval 
      engine.put("x",i); 
      total += (double)engine.eval(function); 
     } 

     return total; 
    } 

} 
+0

值得注意的是,對於(double i = lower; i

+0

我不能爲我的生活想象爲什麼任何人會downvote這個問題。 Erik提供了他的代碼,他遇到的問題以及他收到的確切錯誤消息。 SO用戶可能會問什麼更多問題? –

回答

3

Nashorn使用optimistic typing(因爲JDK 8u40),所以它會在不需要雙打時使用整數。因此,你不能指望它返回一個Double。

另外,5*x^2表示JavaScript中的「五次x xor二次」。 **指數運算符在JavaScript語言的更新版本中定義,但Nashorn尚不支持它。

如果你改變你的JavaScript代碼5*x*x它會工作,但它是更安全的事:

total += 0.001 * ((Number)engine.eval(function)).doubleValue(); 

編譯常用代碼的

既然你在一個循環中重複調用這個函數,最好的做法是事先編譯這個函數。這種性能優化並不是絕對必要的,但是因爲引擎必須每次編譯你的函數(儘管它可能使用緩存來幫助實現這一點)。

import javax.script.Compilable; 
import javax.script.CompiledScript; 
import javax.script.Invocable; 
import javax.script.ScriptContext; 

CompiledScript compiledScript = ((Compilable)engine) 
    .compile("function func(x) { return " + function + "}"); 
compiledScript.eval(compiledScript.getEngine() 
    .getBindings(ScriptContext.ENGINE_SCOPE)); 

Invocable funcEngine = (Invocable) compiledScript.getEngine(); 

// . . . 

total += 0.001 * ((Number)funcEngine.invokeFunction("func", i)).doubleValue(); 

使用ES6語言功能

在未來,當犀牛確實支持**運營商,如果你想使用它,你可能需要打開ES6這樣的特點:

import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 

NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 
ScriptEngine enjin = factory.getScriptEngine("--language=es6"); 

或者這樣:

java -Dnashorn.args=--language=es6 

*編輯來說明評論中指出的數學修正。

+0

所以這個解決方案也運行良好,但我遇到了另一個問題。由於Java,似乎有一個非常大的舍入誤差,因爲當我評估這個時,我得到了205.867而不是208和1/3,這是真正的解決方案。有沒有辦法解決? –

+0

這不是四捨五入錯誤,但數學錯誤:)請參閱我的更新的答案爲該部分上的修復。 –

2

你的JS代碼片段返回Integer(*),因爲x^2不是爲了2的冪在JavaScript的正確方法。改爲嘗試5*Math.pow(x,2),表達式將返回Double

在JavaScript中,^運算符是bitwise XOR

而且循環計算的積分是錯誤的,你需要的矩形寬度乘以:

double delta = 0.001; 
    for (double i = lower; i < upper; i += delta) { 
     //evaluates the interval 
     engine.put("x", i); 
     total += delta * ((Number) engine.eval(function)).doubleValue(); 
    } 

(*)的初步說明,請參見David's answer。但是在評論中,@A.Sundararajan提供了證據。我沒有調查確切的原因,我只注意到我得到了一個Integer,並且只是猜測表達式中的按位運算(來自OP的原始代碼)的使用正在觸發轉換爲整數。我原本編輯我的帖子,以包括「數學錯誤」的修復,但大衛的新答案(約4分鐘^^)是更完整的原始問題,並應保持接受答案恕我直言。

+0

所以這似乎工作,但我得到一個重要的舍入錯誤或什麼的。我也將矩形長度乘以.001。 –

+0

是的代碼丟失了delta(矩形寬度)的乘法,但是這樣我得到了預期的錯誤。例如,如果我在[0,1]區間將表達式簡化爲'x * x',則精確結果爲'1/3',對於delta ='0.001',我得到'0.33283350000000095',對於delta ='',我得到'0.3333833349999431' 0.0001'。這對我來說看起來「沒問題」。 –

+0

優秀的數學工作,Hugues! –

相關問題