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

Javascript 如何在jjs/Nashorn中使用java.math.biginger?,javascript,java,biginteger,fibonacci,nashorn,Javascript,Java,Biginteger,Fibonacci,Nashorn,我想在nashorn/jss JavaScript中使用java.math.biginger 举个例子,假设我想计算斐波那契数列。即使数字变得非常大,也需要保持准确 工作Java代码如下所示: public static BigInteger fibonacci(int n) { BigInteger prev = new BigInteger("0"); if (n == 0) return prev; BigInteger next = new BigInteger("1");

我想在nashorn/jss JavaScript中使用java.math.biginger

举个例子,假设我想计算斐波那契数列。即使数字变得非常大,也需要保持准确

工作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;
}
输出是
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));
将输出:

  • 999999999999999 8:999999999999 8
  • 999999999999999999:1000000000000000
我不确定这是不是“特雷肖尔德”的问题,或者是某些特定的数字有不准确的地方

更新2:

这现在被报告为一个bug: Bug报告是由Oracle JDK/Nashorn开发人员完成的,所以我想这是真实的。祝我好运。

我以你为例:

var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
在调试模式下启动程序,并注意到未使用
biginger
toString
方法。所以我创建了一个简单的类:

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在将
biginger
转换为
String
之前使用了一些中间转换,因此我在
biginger.doubleValue()
处创建了一个断点,并在打印bare
biginger
时触发。下面是有问题的堆栈跟踪,让您了解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());
}

是的,这是一个问题。已提交一个bug->

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)));
函数斐波那契(n){
var biginger=Java.type(“Java.math.biginger”);
prev=新的大整数(“0”);
如果(n==0)返回上一个;
next=新的大整数(“1”);
如果(n==1),则返回next;
变量i,fib=null;
对于(i=1;i
你有没有试着为这个bug寻找一个阈值(在阈值之下,工作正常,在阈值之上,bug)?
我不确定这是一个“treshold”的问题,或者是一些特定的数字有不准确的问题,尽管
如果下一个数字有同样的问题,下一个,下一个等等,这是一个阈值,否则就是别的了。@Dici。好啊但是有没有办法克服这个问题呢?没有,我只是想了解这个问题的本质problem@Dici我的猜测是,biginger(和BigDecimal)看起来像javascript数字,如果您理解我的意思,那么计算“太快了”。如果获取更新代码,并将toString()附加到BigInteger实例,则输出仍然相同。如果将toString()应用于实际的java对象,这是不可能的。所以我怀疑bigInteger首先被转换成JavaScript数字,失去了精度,然后对该JavaScript数字调用toString()。非常感谢!是的,我也怀疑在javascript端,我们实际上不是在谈论Java BigInteger对象,而是已经转换为javascript数字的东西。你怎么认为?这是虫子吗?有没有其他方法可以在不必自己添加Java类的情况下仍然使用BigInteger?@RolandBouman,Nashorn内部的所有类型都会自动被
JSType
版本的
toString
覆盖,所以不幸的是,如果没有外部类,我看不到打印值的方法,调用
toString(基数)
也被
JSType
截取。感谢您的分析。该死,这真是令人失望——JSType.toStringImpl是邪恶的。在我看来,它没有必要自行决定这个双重值是否足够好。我将这个问题转发给了nashorn-dev邮件列表。我希望他们能回应并解决这个问题。了不起的我意识到这很难看,但这没关系——解决办法通常是可行的,有一些有效的解决方案比什么都没有要好得多。然而,如果决定Nashorn不会改变它处理大数字的行为,那么这个工作创伤将需要在这里重点记录。实际上,javaToString函数的更直接的实现可以使用java.util.Objects.toString方法。
if (obj instanceof Number) {
    return toString(((Number)obj).doubleValue());
}
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)));