简体   繁体   English

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

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

I would like to use java.math.BigInteger in a nashorn / jss JavaScript. 我想在nashorn / jss JavaScript中使用java.math.BigInteger。

By way of example, let's say I want to calculate Fibonacci sequence numbers. 举例来说,假设我想计算Fibonacci序列号。 Numbers will need to remain exact, even if they become very large. 数字将需要保持精确,即使它们变得非常大。

Working Java code looks like this: 工作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;
}

We can test with: 我们可以测试:

  • n=77: 5527939700884757 n = 77:5527939700884757
  • n=78: 8944394323791464 n = 78:8944394323791464
  • n=79: 14472334024676221 n = 79:14472334024676221

So far so good. 到现在为止还挺好。

Equivalent JavaScript code below: 下面的等效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;
}

Now we get: 现在我们得到:

  • n=77: 5527939700884757 n = 77:5527939700884757
  • n=78: 8944394323791464 n = 78:8944394323791464
  • n=79: 14472334024676220 n = 79:14472334024676220

Note that the value for 79 is one off - it's wrong. 请注意,79的值是一次性 - 这是错误的。

I suspect the problem is that somewhere, the BigNumber values are re-interpreted as plain JavaScript Numbers. 我怀疑问题是在某处,BigNumber值被重新解释为纯JavaScript编号。 (by "somewhere" I suspect this already happens as the supposedly BigInteger is passed to the .add method) (通过“某个地方”我怀疑这已经发生,因为所谓的BigInteger被传递给.add方法)

For example, if I you do: 例如,如果我这样做:

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

The output is 14472334024676220 , not 14472334024676221 . 输出为14472334024676220 ,而不是14472334024676221 This happens even if I explicitly call .toString() on the BigInteger object. 即使我在BigInteger对象上显式调用.toString() ,也会发生这种情况。

How do I get past this? 我如何通过这个?

UPDATE: @Dici asked if I looked for a threshold. 更新:@Dici问我是否找了一个门槛。 I did - I found: 我做了 - 我发现:

var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + new BigInteger(str));
str = "9999999999999999";
print(str + ": " + new BigInteger(str));

will output: 将输出:

  • 9999999999999998: 9999999999999998 9999999999999998:9999999999999998
  • 9999999999999999: 10000000000000000 9999999999999999:10000000000000000

I'm not sure it it's a matter of "treshold", or of some particular numbers having inaccuracies though. 我不确定它是“门槛”的问题,还是某些特定数字有不准确的问题。

UPDATE 2: 更新2:

This is now reported as a bug: https://bugs.openjdk.java.net/browse/JDK-8146264 Bug report was done by a Oracle JDK/Nashorn developer so I guess it's the real thing. 现在报告为一个错误: https//bugs.openjdk.java.net/browse/JDK-8146264错误报告由Oracle JDK / Nashorn开发人员完成,所以我猜这是真的。 Keeping my fingers crossed. 我的手指交叉。

I took your example: 我举了你的例子:

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

Started the program in debug mode and noticed that toString method of BigInteger was not used. 在调试模式下启动程序并注意到没有使用BigInteger toString方法。 So I created a simple class: 所以我创建了一个简单的类:

public class ToString {
    private final BigInteger x;

    public ToString(BigInteger x) {
        this.x = x;
    }

    @Override
    public String toString() {
        return x.toString();
    }
}

And used it in order to output the BigInteger , and it worked: 并使用它来输出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

Then I suspected that Nashorn used some intermediate conversion before converting BigInteger to String so I created a breakpoint at BigInteger.doubleValue() and it triggered when bare BigInteger was printed. 然后我怀疑Nashorn在将BigInteger转换为String之前使用了一些中间转换,所以我在BigInteger.doubleValue()创建了一个断点,并在打印裸BigInteger时触发。 Here is the problematic stack trace to let you understand Nashorn's logic: 这是有问题的堆栈跟踪,让您了解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)

And the problematic Nashorn's code JSType.toStringImpl : 有问题的Nashorn的代码JSType.toStringImpl

if (obj instanceof Number) {
    return toString(((Number)obj).doubleValue());
}

Yes, this is an issue. 是的,这是一个问题。 A bug has been filed -> https://bugs.openjdk.java.net/browse/JDK-8146264 已经提交了一个错误 - > https://bugs.openjdk.java.net/browse/JDK-8146264

JSType and few other places have "instanceof Number" check -- not sure if fixing JSType.toStringImpl alone will do. JSType和其他一些地方都有“instanceof Number”检查 - 不确定是否单独修复JSType.toStringImpl。 In any case, I've a workaround - not very pretty one - but a workaround nevertheless. 无论如何,我有一个解决方法 - 不是很漂亮 - 但仍然是一个解决方法。 You can call java.lang.Object.toString method on those objects thereby avoiding Nashorn's JSType string conversion code. 您可以对这些对象调用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)));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM