为什么 eval class 给我一个从 int 到 double 的转换错误?
Why is the eval class giving me a casting error from int to double?
我正在尝试制作一个采用字符串公式的方法,并通过以非常小的间隔计算黎曼求和来求解该公式的积分。我正在使用 ScriptEngine 和 ScriptEngineManager 类 来评估函数(使用 eval() 方法)。出于某种原因,我收到此错误:
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;
}
}
您的 JS 代码段 return 是 Integer
(*),因为 x^2
不是在 JavaScript 中获得 2 的幂的正确方法。尝试使用 5*Math.pow(x,2)
,表达式将 return a 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();
}
(*) 有关初步解释,请参阅 。但在评论中,@A.Sundararajan 提供了反对这一点的证据。我没有调查确切的原因,我只观察到我得到了一个 Integer
,并且只是猜测在表达式中使用按位运算(来自 OP 的原始代码)触发了到整数的转换。我最初编辑了我的 post 以包含 "math error" 的修复程序,但 David 的新答案(大约 4 分钟 ^^)对于原始问题更完整,并且应该保留接受的答案恕我直言。
Nashorn 使用 optimistic typing (since JDK 8u40),因此当不需要双精度数时它将使用整数。因此,您不能指望它返回 Double。
此外,5*x^2
表示JavaScript中的"five times x xor two"。 **
求幂运算符在 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 语言特性
将来,当 Nashorn 支持 **
运算符时,如果你想使用它,你可能需要像这样打开 ES6 功能:
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");
或者像这样:
java -Dnashorn.args=--language=es6
* 已编辑以说明评论中指出的数学修正。
我正在尝试制作一个采用字符串公式的方法,并通过以非常小的间隔计算黎曼求和来求解该公式的积分。我正在使用 ScriptEngine 和 ScriptEngineManager 类 来评估函数(使用 eval() 方法)。出于某种原因,我收到此错误:
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;
}
}
您的 JS 代码段 return 是 Integer
(*),因为 x^2
不是在 JavaScript 中获得 2 的幂的正确方法。尝试使用 5*Math.pow(x,2)
,表达式将 return a 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();
}
(*) 有关初步解释,请参阅 Integer
,并且只是猜测在表达式中使用按位运算(来自 OP 的原始代码)触发了到整数的转换。我最初编辑了我的 post 以包含 "math error" 的修复程序,但 David 的新答案(大约 4 分钟 ^^)对于原始问题更完整,并且应该保留接受的答案恕我直言。
Nashorn 使用 optimistic typing (since JDK 8u40),因此当不需要双精度数时它将使用整数。因此,您不能指望它返回 Double。
此外,5*x^2
表示JavaScript中的"five times x xor two"。 **
求幂运算符在 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 语言特性
将来,当 Nashorn 支持 **
运算符时,如果你想使用它,你可能需要像这样打开 ES6 功能:
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");
或者像这样:
java -Dnashorn.args=--language=es6
* 已编辑以说明评论中指出的数学修正。