1

I am using the following function to try to create a 64-bit hash of a string, but it is failing with an ArithmeticException even though I am using the "unchecked" version of the arithmetic operators.

user> (reduce (fn [h c]
                (unchecked-add (unchecked-multiply h 31) (long c)))
              1125899906842597
              "hello")
ArithmeticException integer overflow   clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)

What am I doing wrong here?

2 Answers 2

3

have a hint here:

for whatever reason the first param in a function here is treated as integer. Adding type hint helps to solve this problem:

user> (reduce (fn [^long h c]
                (unchecked-add (unchecked-multiply h 31) (long c)))
              1125899906842597
              "hello")
7096547112155234317

update: moreover: it looks that it comes from the unchecked-multiply

user> (reduce (fn [h c]
                (unchecked-add (unchecked-multiply ^long h 31) (long c)))
              1125899906842597
              "hello")
7096547112155234317

i will make some additional research, and update here, in case of any new information

update 2: ok, that's what i've found out:

looking at the clojure's documentation at https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Numbers.java we can see the following:

our case

static public Number unchecked_multiply(Object x, long y){return multiply(x,y);}

leads to:

static public Number multiply(Object x, long y){
    return multiply(x,(Object)y);
}

then:

static public Number multiply(Object x, Object y){
    return ops(x).combine(ops(y)).multiply((Number)x, (Number)y);
}

so at the end it calls multiply method from LongOps inner class.

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

so finally it leads us to a simple (checked?) multiply:

static public long multiply(long x, long y){
  if (x == Long.MIN_VALUE && y < 0)
        return throwIntOverflow();
    long ret = x * y;
    if (y != 0 && ret/y != x)
        return throwIntOverflow();
    return ret;
}

kaboom!

so i don't know whether it is a bug or the desired behavior, but it looks really weird to me.

so the only thing i could advice, is to always remember to typehint your values when using unchecked math in clojure.

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

2 Comments

a (println h c) here also might help to see whats going on (h is growing)
Thanks. I got caught in the web of clojure.lang.Numbers myself. Nice catch. It certainly looks like a bug to me - but I'm no expert.
1

You can get the behaviour you want by avoiding the function calls:

(loop [h 1125899906842597
       cs "hello"]
  (let [c (first cs)]
    (if c
      (recur (unchecked-add (unchecked-multiply h 31) (long c))
             (rest cs))
      h)))

;7096547112155234317

Why this is so, I don't know.

3 Comments

that is probably because h here is obviously a number, and the compiler is smart enough to see that, while in a function the type of the parameter without a hint is considered to be the Object.
@leetwinski Yes. The puzzle is not why this works, but why the questioner's version doesn't. Why should a boxed longbe regarded as a boxed int? Your answer makes a good case that this is a bug.
It's not considered a boxed int, the method just calls throwIntOverflow, while both operands are long.

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.