?: promotion of integral types

17 views
Skip to first unread message

alux

unread,
May 5, 2010, 3:15:23 AM5/5/10
to Clojure
Hello,

can somebody please explain the difference between the following two?

(def imax (Integer/MAX_VALUE))
(+ imax imax)

gives 4294967294

but

(+ (Integer/MAX_VALUE) (Integer/MAX_VALUE))

results in integer overflow
[Thrown class java.lang.ArithmeticException]

Shouldnt this be the same?

Thank you, alux

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Sean Devlin

unread,
May 5, 2010, 8:44:12 AM5/5/10
to Clojure
Yeah, I just reproduced this. I think it's a bug. Created Assembla
ticket 339. I've added some more test cases their too.

Thanks!
Sean

AlexK

unread,
May 5, 2010, 9:38:03 AM5/5/10
to Clojure
That's a wierd behviour, but it never produces incorrect results (it
throws an exception when it would).

the two-argument versions of + - * / get inlined to a static method
call. Either to a static method which accepts unboxed primitives or
one that accepts their boxed variants. The problem is that the
primitive versions return primitives and thus cannot overflow to
Bignums, so they throw an exception. This 'bug' is unsolvable for now
(until we get Fixnums on the JVM) without sacrificing the speed of
primitive math.

AlexK

Sean Devlin

unread,
May 5, 2010, 10:10:09 AM5/5/10
to Clojure
When this doesn't work:

(=
(+ Integer/MAX_VALUE Integer/MAX_VALUE)
(+ Integer/MAX_VALUE Integer/MAX_VALUE 0))

You have a bug.

On May 5, 9:38 am, AlexK <alexander.konstanti...@informatik.haw-

Sean Devlin

unread,
May 5, 2010, 10:32:16 AM5/5/10
to Clojure
And I was too quick to post. Sorry about that.

You've got the unchecked addition fn for speed, and those are allowed
the throw overflow errors. The + fn is always supposed to work. The
fact that it auto-promotes the bound version is proof. It needs to
work with the literal, too.

It's a Clojure issue, not a JVM one. That's why this needs to be
fixed.

Sean

David Nolen

unread,
May 5, 2010, 10:47:43 AM5/5/10
to clo...@googlegroups.com
On Wed, May 5, 2010 at 10:32 AM, Sean Devlin <francoi...@gmail.com> wrote:
And I was too quick to post.  Sorry about that.

You've got the unchecked addition fn for speed, and those are allowed
the throw overflow errors.  The + fn is always supposed to work.  The
fact that it auto-promotes the bound version is proof.  It needs to
work with the literal, too.

It's a Clojure issue, not a JVM one.  That's why this needs to be
fixed.

Sean

Primitive types are only preserved in let bindings. From what I can tell the primitive int gets promoted to an Integer when placed in a var. + is not auto promoting, storing the primitive in a var is auto promoting. Thus + works fine in the first case. In the second case Integer/MAX_VALUE is a Java static method so it's primitive return type is known - int, thus the overflow error. For example:

(def a (int 1))
(def b (int 2))

; slow generic math
(dotimes [_ 10]
  (time
   (dotimes [_ 1000000]
     (+ a b))))

; let binding preserves primitive type
; fast math
(let [a (int 1)
      b (int 2)]
 (dotimes [_ 10]
   (time
    (dotimes [_ 1000000]
      (+ a b)))))

So the following will also throw an overflow exception:

(def max-int Integer/MAX_VALUE)
(+ (int max-int) (int max-int))

So this seems like programmer error to me, but again that's because what returns primitive types and where primitive types are preserved is not totally intuitive (though pretty well documented I think)

David

Sean Devlin

unread,
May 5, 2010, 11:11:43 AM5/5/10
to Clojure
I think there's a fundamental assumption that I disagree with.

Since we've already opened the can of worms that is auto-promotion, it
should *always* work. Given auto-promotion, + shouldn't be fast (use
unchecked-add), it should be predictable. Same for -, * and friends.
Quality is more important than performance, principle of least
surprise, etc.

Sean

On May 5, 10:47 am, David Nolen <dnolen.li...@gmail.com> wrote:

David Nolen

unread,
May 5, 2010, 11:32:42 AM5/5/10
to clo...@googlegroups.com
On Wed, May 5, 2010 at 11:11 AM, Sean Devlin <francoi...@gmail.com> wrote:
I think there's a fundamental assumption that I disagree with.

Since we've already opened the can of worms that is auto-promotion, it
should *always* work.  Given auto-promotion, + shouldn't be fast (use
unchecked-add), it should be predictable.  Same for -, * and friends.
Quality is more important than performance, principle of least
surprise, etc.

Sean

unchecked arithmetic operations are discouraged because ... they are unchecked. + is fast *and* it checks for overflow on primitive types. Again + is not doing any auto-promoting here. Putting a primitive in a var is. Case 1 in the OP is the surprise for those who don't know that. Case 2 is the correct behavior that I've come to expect from + when passing in primitive types.

For those who rely on fast arithmetic, having +, * and friends be slow is simply unacceptable (and that's not a small number of Clojurians, and it should include anyone wishes to see a performant Clojure written in itself).

This is a conceptual break from some other dynamic languages - but then the performance guarantees of those languages is nothing to brag about either. Clojure ships with great performance guarantees - that's a feature.

David 

alux

unread,
May 5, 2010, 2:43:24 PM5/5/10
to Clojure
Thank you for the discussion - even if I dont understand it
immediately ;-)

Grettings, alux

On 5 Mai, 17:32, David Nolen <dnolen.li...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages