Как использовать java.математика.BigInteger в jjs / Nashorn?
Я хотел бы использовать java.математика.BigInteger в JavaScript nashorn / jss.
В качестве примера предположим, что я хочу вычислить последовательные числа Фибоначчи. Числа должны оставаться точными, даже если они становятся очень большими.Рабочий код 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: 14472334024676221
Пока все хорошо.
Эквивалент Код 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: 14472334024676220
Например, если я вас сделайте:
var BigInteger = Java.type("java.math.BigInteger");
print(new BigInteger("14472334024676221"));
Выход будет 14472334024676220
, а не 14472334024676221
.
Это происходит, даже если я явно вызываю .toString()
на объекте BigInteger.
Как мне пройти через это?
UPDATE: @Dici спросил, ищу ли я порог. Я сделал - я нашел:
var str, BigInteger = Java.type("java.math.BigInteger");
str = "9999999999999998";
print(str + ": " + new BigInteger(str));
str = "9999999999999999";
print(str + ": " + new BigInteger(str));
Выведет:
- 9999999999999998: 9999999999999998
- 9999999999999999: 10000000000000000
Я не уверен, что это вопрос "трешхолда" или каких-то конкретных чисел, имеющих неточности хотя.
Обновление 2:
Теперь об этом сообщается как об ошибке: https://bugs.openjdk.java.net/browse/JDK-8146264 Отчет об ошибке был сделан разработчиком Oracle JDK / Nashorn, поэтому я думаю, что это реальная вещь. Держу пальцы скрещенными.
2 ответа:
Да, это проблема. Ошибка была подана -> https://bugs.openjdk.java.net/browse/JDK-8146264
JSType и несколько других мест имеют проверку "instanceof Number" - не уверен, что исправляет JSType.тострингимпл один справится. В любом случае, у меня есть обходной путь - не очень красивый, но все же обходной путь. Вы можете позвонить на яву.яз..Объект.метод toString на этих объектах, таким образом, избегая кода преобразования строки Jstype Nashorn.
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"));
Запустил программу в режиме отладки и заметил, что метод
toString
BigInteger
не используется. Поэтому я создал простой класс: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
Затем я заподозрил, что Нашорн использовал некоторое промежуточное преобразование перед преобразованием
BigInteger
вString
, поэтому я создал точку останова вBigInteger.doubleValue()
, и она сработала, когда был напечатан голыйBigInteger
. Вот проблемная трассировка стека, чтобы вы поняли Логика нэшорна: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)
И проблемный код Нэшорна
JSType.toStringImpl
:if (obj instanceof Number) { return toString(((Number)obj).doubleValue()); }