Как избежать "переполнения целочисленного массива ArithmeticException" в Clojure?

Кажется, это происходит все время. Например:

(apply * (range 1 101))

дает ошибку

ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374)

Пока в Ruby 1.9.2 (концептуально) эквивалентный код,

(1..100).reduce(:*)

дает желаемый результат

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Очевидно, что два языка сильно различаются под капотом, но похоже, что оба они могут справиться с этим вычислением без проблем. Я здесь что-то не так? Или я неправильно понимаю?

Ответы

Ответ 1

Вам нужно использовать какую-то форму BigInteger.

Попробуйте (apply *' (range 1 101)).

(см. http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics - очевидно, что это автоматически способствует переполнению?)

Ответ 2

Ruby имеет автоматическое продвижение вычислений, меняя его на большее число биттипов, когда результат переполняет его тип. Из соображений производительности вычисления с Clojure 1.3 вверх не будут автоматически повышаться, и вам нужно учитывать, может ли вычисление переполняться, или использовать одну из автоматических математических функций (+', -', *', /'), если производительность не будет проблемой.

Ответ 3

Из Clojure 1.3 вы должны явно указывать Integer (или Long) на BigInt, иначе при слишком большом числе будет ошибка "Переполнение целых чисел ArithmeticException".

Есть три решения, пожалуйста, выберите тот, который вы предпочитаете:

  • используйте одну из автоматических математических функций: +', -', *', /', inc', dec'

    Пример: (apply *' (range 1 101))

  • используйте функцию заливки типа BigInt

    Пример: (apply * (range (bigint 1) 101))

  • изменить числовой литерал BigInt

    пример: (apply * (range 1N 101))

Ссылка: Документация для Clojure 1.3 Числа

Ответ 4

Наткнулся на ту же ситуацию. Для меня использование "1.0" вместо "1" сделало трюк.

(apply * (range 1.0 1000))

Поскольку документировано, существует разница между поплавками и десятичными знаками.

Ответ 5

Я не знаю Clojure, но похоже, что он использует 32-битные или 64-разрядные целые числа для вычисления. 32-разрядные целые числа со знаком имеют максимальное значение 2^31 - 1 (бит более 2 * 10 ^ 9). 64-битные целые числа имеют максимальное значение 2^64 - 1 (бит более 4 * 10 ^ 18). Значение, которое вы получили от Ruby, намного выше.

Очевидно, Ruby использует некоторое другое представление числа под капотом.

Ответ 6

в 1.3.0

(применить * (диапазон 1N 101N)); clojure.lang.BigInt

(применить * (диапазон 1M 101M)); java.math.BigDecimal