如何在jjs/Nashorn中使用java.math.BigInteger

How to use java.math.BigInteger in jjs / Nashorn?

本文关键字:java math BigInteger Nashorn jjs      更新时间:2023-09-26

我想在nashorn/jss JavaScript中使用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:144772334024676221

到目前为止还不错。

以下等效JavaScript代码:

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:144772334024676220

请注意,79的值是一次性的,这是错误的。

我怀疑问题在于,在某些地方,BigNumber值被重新解释为普通的JavaScript数字。(通过"某处",我怀疑当所谓的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));

将输出:

  • 99999999999 8:999999999999999 8
  • 9999999999999999:10000000000000000

我不确定这是"非法侵入"的问题,或者某些特定的数字有不准确之处。

更新2:

现在报告为一个错误:https://bugs.openjdk.java.net/browse/JDK-8146264Bug报告是由Oracle JDK/Nashorn开发人员完成的,所以我想这是真实的。祈祷吧。

是的,这是一个问题。已提交错误->https://bugs.openjdk.java.net/browse/JDK-8146264

JSType和其他一些地方都有"instanceofNumber"检查——不确定单独修复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)));

我举了你的例子:

var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));

在调试模式下启动程序,注意到没有使用BigIntegertoString方法。所以我创建了一个简单的类:

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.stackoverflow.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());
}