JDK-8146264 : BigInteger precision is lost in nashorn
  • Type: Bug
  • Component: core-libs
  • Sub-Component: jdk.nashorn
  • Affected Version: 9
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2015-12-29
  • Updated: 2016-01-12
  • Resolved: 2016-01-12
Related Reports
Duplicate :  
Relates :  
Description
This bug was reported via nashorn-dev. See also http://mail.openjdk.java.net/pipermail/nashorn-dev/2015-December/005783.html

The test cut-pasted here for convenience. 

Example of a Java function I would like to get working in Nashorn / jss javascript below:

  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 these values:

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

So far so good. Now I try the - what I think is - equivalent function in
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;
}

However, now we get different results:

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

Note that the value for n=79 is off by one.

Comments
This is a duplicate of JDK-8143896. Although JDK-8143896 is about longs, it fixes the problem for all instances of java.lang.Number that exceed the precision of a double. The test for JDK-8143896 also covers java.math.BigInteger and java.math.BigDecimal.
12-01-2016

I think since we treat any java.lang.Number subtype as JS "number" -- for conversion + arithmetic, it is better *not* to fix "String" conversion to be precise. Because if we fix String conversion and still use double conversion for arithmetic, users may get confused. Users will see BigInteger/BigDecimal being precisely printed (by auto String conversion)-- but arithmetic is being still performed as "double". I'm ambivalent if that is desirable at all. Allowing explicit java calls to return precise output is different from auto String conversion of JS being precise. I'm inclined to have JDK-8143896 fix checked in and close the current issue as "will-not-fix" and document the behaviour (that people should call .toString java method to get precise conversion). We need to discuss this with the team.
30-12-2015

With fix proposed for JDK-8143896 (http://mail.openjdk.java.net/pipermail/nashorn-dev/2015-December/005758.html), Java methods will be called for any Number subtype other than Double and Integer (the types internally used by Nashorn represent JS "number" type). With that fix, users can call toString on BigDecimal and BigInteger directly and get the desired precise representation.
30-12-2015

A straightforward implementation of javaToString function could be: function javaToString(obj) { return java.util.Objects.toString(obj) } It does not have to use Function.call.call etc.
30-12-2015

Workaround suggested here -> http://stackoverflow.com/questions/34502968/how-to-use-java-math-biginteger-in-jjs-nashorn/34512074#34512074 Workaround cut-pasted here for convenience: 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)));
29-12-2015