7

I'm running Clojure 1.4.0. Why is it if I add Integer/MAX_VALUE and 1, I get a Long, but if I add Integer/MAX_VALUE to itself, I get an exception?

=> (def one 1)
=> (class one)
java.lang.Integer
=> (def max-plus-one (+ Integer/MAX_VALUE one))
=> max-plus-one
2147483648
=> (class max-plus-one)
java.lang.Long

=> (+ Integer/MAX_VALUE Integer/MAX_VALUE)
java.lang.ArithmeticException: integer overflow (NO_SOURCE_FILE:0)

Shouldn't they both act the same way? Why does adding two MAX_VALUE values overflows but adding 1 doesn't?

I've seen this SO question but they are getting different behaviour than I am.

3 Answers 3

7

That's strange, I see different results with Clojure 1.4.0and Java(TM) SE Runtime Environment (build 1.7.0_06-b24), on Ubuntu 12.04 64bit:

user=> *clojure-version*
{:major 1, :minor 4, :incremental 0, :qualifier nil}
user=> (+ Integer/MAX_VALUE Integer/MAX_VALUE)
4294967294
user=> (type 1)
java.lang.Long
user=> (def max-plus-one (+ Integer/MAX_VALUE one))
#'user/max-plus-one
user=> max-plus-one
2147483648
user=> (type max-plus-one)
java.lang.Long
user=> (+ Integer/MAX_VALUE Integer/MAX_VALUE)
4294967294

You can always check the Java classes which clojure.core uses for numerics, to see how the functionality is implemented:

The implementation of the + operator in:

(defn +
  "Returns the sum of nums. (+) returns 0. Does not auto-promote
  longs, will throw on overflow. See also: +'"
  {:inline (nary-inline 'add 'unchecked_add)
   :inline-arities >1?
   :added "1.2"}
  ([] 0)
  ([x] (cast Number x))
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
     (reduce1 + (+ x y) more)))

Java implementation of adding longs:

final public Number add(Number x, Number y){
    return num(Numbers.add(x.longValue(),y.longValue()));
}

Edits: Tested with Clojure 1.2.1
I have done a quick test with Clojure 1.2.1, and with that version of Clojure I get exactly your behavior.

user=> *clojure-version*
{:major 1, :minor 2, :incremental 1, :qualifier ""}
user=> (def one 1)
#'user/one
user=> (class 1)
java.lang.Integer
user=> (def max-plus-one (+ Integer/MAX_VALUE one))
#'user/max-plus-one
user=> max-plus-one
2147483648
user=> (class max-plus-one)
java.lang.Long
user=> (+ Integer/MAX_VALUE Integer/MAX_VALUE)
java.lang.ArithmeticException: integer overflow (NO_SOURCE_FILE:0)

I'd say that you did the test with Clojure 1.2.x, and not with 1.4.0. What is the value of *clojure-version* in your REPL?

Sign up to request clarification or add additional context in comments.

4 Comments

You're right: {:major 1, :minor 2, :incremental 1, :qualifier ""}. I have 1.4.0 installed but it appears the Sublime Text 2 REPL uses its own Clojure.
I've had a similar experience in the past, when I had multiple versions of Clojure on my machine.
Yeah it looks like the SublimeREPL plugin runs lein repl which is loading up Clojure 1.2.1 for some reason.
Ok, for people curious as to why the version of lein repl is different than your clj version: github.com/technomancy/leiningen/issues/337 and stackoverflow.com/questions/10135440/…
4

Looks like you have your answer, but here are a few other interesting points:

java (all versions) and clojure's (>1.3.0) default behaviour differ in their behaviour wrt overflow

in java

(Long.MAX_VALUE + 1) == Long.MIN_VALUE
(Integer.MAX_VALUE + 1) == Integer.MIN_VALUE

// cast required to avoid promoting to int
(Byte.MAX_VALUE + (byte)1) == Byte.MIN_VALUE 

This is because arithmetic wraps by default on the jvm

in clojure (>1.3.0)

(inc Long.MAX_VALUE) 
   => ArithmeticOverflow

(inc Integer/MAX_VALUE) 
   => a long with value Integer/MAX_VALUE + 1
(int (inc Integer/MAX_VALUE)) 
   => IllegalArgumentException Value 
      out of range for int: 2147483648 

clojure does have versions of some ops that behave like java

(unchecked-inc Long.MAX_VALUE) => Long.MIN_VALUE

You can make the unchecked operations the default by setting *unchecked-math* to true

(set! *unchecked-math* true)
(inc Long/MAX_VALUE) 
    => (Long.MIN_VALUE)
(int (inc Integer/MAX_VALUE)) 
    => (Integer.MIN_VALUE) of type Integer

There are lots of other interesting (unchecked-*) operations.

Comments

1

As of version 1.3.0 Clojure uses Longs for all primitive numbers. you just need to use larger numbers to get the overflow.

 (def max-plus-one (+ Long/MAX_VALUE one))

1 Comment

The strange thing is that (class 1) or (type 1) seems to return 'java.lang.integer' instead of long in cdmckay's case. If he's running Clojure 1.4.0, that shouldn't happen.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.