Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I would like to use java.math.BigInteger in a nashorn / jss JavaScript.

By way of example, let's say I want to calculate Fibonacci sequence numbers. Numbers will need to remain exact, even if they become very large.

Working Java code looks like this:

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=78: 8944394323791464
  • n=79: 14472334024676221

So far so good.

Equivalent JavaScript code below:

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=78: 8944394323791464
  • n=79: 14472334024676220

Note that the value for 79 is one off - it's wrong.

I suspect the problem is that somewhere, the BigNumber values are re-interpreted as plain JavaScript Numbers. (by "somewhere" I suspect this already happens as the supposedly BigInteger is passed to the .add method)

For example, if I you do:

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

The output is 14472334024676220, not 14472334024676221. This happens even if I explicitly call .toString() on the BigInteger object.

How do I get past this?

UPDATE: @Dici asked if I looked for a threshold. 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
  • 9999999999999999: 10000000000000000

I'm not sure it it's a matter of "treshold", or of some particular numbers having inaccuracies though.

UPDATE 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. Keeping my fingers crossed.

like image 710
Roland Bouman Avatar asked Dec 13 '25 14:12

Roland Bouman


1 Answers

Yes, this is an issue. A bug has been filed -> 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. 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.

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)));
like image 66
A. Sundararajan Avatar answered Dec 15 '25 02:12

A. Sundararajan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!