如何在 jjs / Nashorn 中使用 java.math.BigInteger?
How to use java.math.BigInteger in jjs / Nashorn?
我想在 nashorn / jss Java脚本中使用 java.math.BigInteger。
举例来说,假设我想计算斐波那契数列。数字需要保持精确,即使它们变得非常大。
工作 Java 代码如下所示:
public static BigInteger fibonacci(int n) {
BigInteger prev = new BigInteger("0");
if (n == 0) return prev;
BigInteger next = new BigInteger("1");
if (n == 1) return next;
BigInteger fib = null;
int i;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
我们可以测试:
- n=77: 5527939700884757
- n=78: 8944394323791464
- n=79: 14472334024676221
到目前为止一切顺利。
等效Java脚本代码如下:
function fibonacci(n) {
var BigInteger = Java.type("java.math.BigInteger");
prev = new BigInteger("0");
if (n == 0) return prev;
next = new BigInteger("1");
if (n == 1) return next;
var i, fib = null;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
现在我们得到:
- n=77: 5527939700884757
- n=78: 8944394323791464
- n=79: 14472334024676220
请注意,79 的值是一个错误 - 这是错误的。
我怀疑问题出在某个地方,BigNumber 值被重新解释为普通的 JavaScript 数字。 (据 "somewhere" 我怀疑这已经发生了,因为所谓的 BigInteger 被传递给了 .add 方法)
例如,如果我这样做:
var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
输出是 14472334024676220
,而不是 14472334024676221
。
即使我在 BigInteger 对象上显式调用 .toString()
,也会发生这种情况。
我该如何克服这个问题?
更新:@Dici 问我是否寻找阈值。我做到了 - 我发现:
var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + new BigInteger(str));
str = "9999999999999999";
print(str + ": " + new BigInteger(str));
将输出:
- 9999999999999998: 9999999999999998
- 9999999999999999: 10000000000000000
我不确定这是 "treshold" 的问题,还是某些特定数字不准确的问题。
更新 2:
这现在被报告为错误:https://bugs.openjdk.java.net/browse/JDK-8146264
错误报告是由 Oracle JDK/Nashorn 开发人员完成的,所以我想这是真的。祈祷吧。
我以你为例:
var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
在调试模式下启动程序,发现未使用 BigInteger
的 toString
方法。所以我创建了一个简单的 class:
public class ToString {
private final BigInteger x;
public ToString(BigInteger x) {
this.x = x;
}
@Override
public String toString() {
return x.toString();
}
}
并使用它来输出 BigInteger
,并且有效:
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineManager.getEngineFactories().get(0).getScriptEngine();
String script = "var BigInteger = Java.type(\"java.math.BigInteger\");\n" +
"var ToString = Java.type(\"com.Whosebug.inner.ToString\");\n" +
"var ts = new ToString(new BigInteger(\"14472334024676221\"));\n" +
"print(ts);";
jsEngine.eval(script); // prints 14472334024676221
然后我怀疑 Nashorn 在将 BigInteger
转换为 String
之前使用了一些中间转换,所以我在 BigInteger.doubleValue()
处创建了一个断点,它在打印裸 BigInteger
时触发.这是有问题的堆栈跟踪,让您了解 Nashorn 的逻辑:
at java.math.BigInteger.doubleValue(BigInteger.java:3888)
at jdk.nashorn.internal.runtime.JSType.toStringImpl(JSType.java:976)
at jdk.nashorn.internal.runtime.JSType.toString(JSType.java:327)
at jdk.nashorn.internal.runtime.JSType.toCharSequence(JSType.java:341)
at jdk.nashorn.internal.objects.NativeString.constructor(NativeString.java:1140)
以及有问题的 Nashorn 代码 JSType.toStringImpl
:
if (obj instanceof Number) {
return toString(((Number)obj).doubleValue());
}
是的,这是一个问题。已提交错误 -> https://bugs.openjdk.java.net/browse/JDK-8146264
JSType 和其他几个地方有 "instanceof Number" 检查——不确定单独修复 JSType.toStringImpl 是否可行。无论如何,我有一个解决方法 - 不是很漂亮 - 但仍然是一个解决方法。您可以在这些对象上调用 java.lang.Object.toString 方法,从而避免 Nashorn 的 JSType 字符串转换代码。
function fibonacci(n) {
var BigInteger = Java.type("java.math.BigInteger");
prev = new BigInteger("0");
if (n == 0) return prev;
next = new BigInteger("1");
if (n == 1) return next;
var i, fib = null;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
function javaToString(obj) {
var javaToStringMethod = (new java.lang.Object()).toString;
var call = Function.prototype.call;
return call.call(javaToStringMethod, obj);
}
print(javaToString(fibonacci(77)))
print(javaToString(fibonacci(78)))
print(javaToString(fibonacci(79)))
var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + javaToString(new BigInteger(str)));
str = "9999999999999999";
print(str + ": " + javaToString(new BigInteger(str)));
我想在 nashorn / jss Java脚本中使用 java.math.BigInteger。
举例来说,假设我想计算斐波那契数列。数字需要保持精确,即使它们变得非常大。
工作 Java 代码如下所示:
public static BigInteger fibonacci(int n) {
BigInteger prev = new BigInteger("0");
if (n == 0) return prev;
BigInteger next = new BigInteger("1");
if (n == 1) return next;
BigInteger fib = null;
int i;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
我们可以测试:
- n=77: 5527939700884757
- n=78: 8944394323791464
- n=79: 14472334024676221
到目前为止一切顺利。
等效Java脚本代码如下:
function fibonacci(n) {
var BigInteger = Java.type("java.math.BigInteger");
prev = new BigInteger("0");
if (n == 0) return prev;
next = new BigInteger("1");
if (n == 1) return next;
var i, fib = null;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
现在我们得到:
- n=77: 5527939700884757
- n=78: 8944394323791464
- n=79: 14472334024676220
请注意,79 的值是一个错误 - 这是错误的。
我怀疑问题出在某个地方,BigNumber 值被重新解释为普通的 JavaScript 数字。 (据 "somewhere" 我怀疑这已经发生了,因为所谓的 BigInteger 被传递给了 .add 方法)
例如,如果我这样做:
var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
输出是 14472334024676220
,而不是 14472334024676221
。
即使我在 BigInteger 对象上显式调用 .toString()
,也会发生这种情况。
我该如何克服这个问题?
更新:@Dici 问我是否寻找阈值。我做到了 - 我发现:
var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + new BigInteger(str));
str = "9999999999999999";
print(str + ": " + new BigInteger(str));
将输出:
- 9999999999999998: 9999999999999998
- 9999999999999999: 10000000000000000
我不确定这是 "treshold" 的问题,还是某些特定数字不准确的问题。
更新 2:
这现在被报告为错误:https://bugs.openjdk.java.net/browse/JDK-8146264 错误报告是由 Oracle JDK/Nashorn 开发人员完成的,所以我想这是真的。祈祷吧。
我以你为例:
var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
在调试模式下启动程序,发现未使用 BigInteger
的 toString
方法。所以我创建了一个简单的 class:
public class ToString {
private final BigInteger x;
public ToString(BigInteger x) {
this.x = x;
}
@Override
public String toString() {
return x.toString();
}
}
并使用它来输出 BigInteger
,并且有效:
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineManager.getEngineFactories().get(0).getScriptEngine();
String script = "var BigInteger = Java.type(\"java.math.BigInteger\");\n" +
"var ToString = Java.type(\"com.Whosebug.inner.ToString\");\n" +
"var ts = new ToString(new BigInteger(\"14472334024676221\"));\n" +
"print(ts);";
jsEngine.eval(script); // prints 14472334024676221
然后我怀疑 Nashorn 在将 BigInteger
转换为 String
之前使用了一些中间转换,所以我在 BigInteger.doubleValue()
处创建了一个断点,它在打印裸 BigInteger
时触发.这是有问题的堆栈跟踪,让您了解 Nashorn 的逻辑:
at java.math.BigInteger.doubleValue(BigInteger.java:3888)
at jdk.nashorn.internal.runtime.JSType.toStringImpl(JSType.java:976)
at jdk.nashorn.internal.runtime.JSType.toString(JSType.java:327)
at jdk.nashorn.internal.runtime.JSType.toCharSequence(JSType.java:341)
at jdk.nashorn.internal.objects.NativeString.constructor(NativeString.java:1140)
以及有问题的 Nashorn 代码 JSType.toStringImpl
:
if (obj instanceof Number) {
return toString(((Number)obj).doubleValue());
}
是的,这是一个问题。已提交错误 -> https://bugs.openjdk.java.net/browse/JDK-8146264
JSType 和其他几个地方有 "instanceof Number" 检查——不确定单独修复 JSType.toStringImpl 是否可行。无论如何,我有一个解决方法 - 不是很漂亮 - 但仍然是一个解决方法。您可以在这些对象上调用 java.lang.Object.toString 方法,从而避免 Nashorn 的 JSType 字符串转换代码。
function fibonacci(n) {
var BigInteger = Java.type("java.math.BigInteger");
prev = new BigInteger("0");
if (n == 0) return prev;
next = new BigInteger("1");
if (n == 1) return next;
var i, fib = null;
for (i = 1; i < n; i++) {
fib = prev.add(next);
prev = next;
next = fib;
}
return fib;
}
function javaToString(obj) {
var javaToStringMethod = (new java.lang.Object()).toString;
var call = Function.prototype.call;
return call.call(javaToStringMethod, obj);
}
print(javaToString(fibonacci(77)))
print(javaToString(fibonacci(78)))
print(javaToString(fibonacci(79)))
var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + javaToString(new BigInteger(str)));
str = "9999999999999999";
print(str + ": " + javaToString(new BigInteger(str)));