为什么即使使用未经检查的数学运算,我的哈希函数也会失败 "ArithmeticException integer overflow"
Why does my hash function fail with "ArithmeticException integer overflow" even when using unchecked math
我正在使用以下函数尝试创建字符串的 64 位散列,但它失败并出现 ArithmeticException,即使我使用的是 "unchecked" 版本的算术运算符。
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)
我做错了什么?
这里有个提示:
无论出于何种原因,此处函数中的第一个参数都被视为整数。添加类型提示有助于解决这个问题:
user> (reduce (fn [^long h c]
(unchecked-add (unchecked-multiply h 31) (long c)))
1125899906842597
"hello")
7096547112155234317
更新:
此外:它看起来来自 unchecked-multiply
user> (reduce (fn [h c]
(unchecked-add (unchecked-multiply ^long h 31) (long c)))
1125899906842597
"hello")
7096547112155234317
我会做一些额外的研究,并在这里更新,以防有任何新信息
更新 2:
好的,这就是我发现的:
在 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Numbers.java 查看 clojure 的文档
我们可以看到以下内容:
我们的案例
static public Number unchecked_multiply(Object x, long y){return multiply(x,y);}
导致:
static public Number multiply(Object x, long y){
return multiply(x,(Object)y);
}
然后:
static public Number multiply(Object x, Object y){
return ops(x).combine(ops(y)).multiply((Number)x, (Number)y);
}
所以最后它从 LongOps
内部 class.
调用 multiply
方法
final public Number multiply(Number x, Number y){
return num(Numbers.multiply(x.longValue(), y.longValue()));
}
所以最后它引导我们进行简单的(已检查?)乘法:
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;
}
轰隆隆!
所以我不知道这是错误还是预期的行为,但我觉得它真的很奇怪。
所以我唯一可以建议的是,在 clojure 中使用未经检查的数学时,请始终记住输入提示您的值。
您可以通过避免函数调用来获得您想要的行为:
(loop [h 1125899906842597
cs "hello"]
(let [c (first cs)]
(if c
(recur (unchecked-add (unchecked-multiply h 31) (long c))
(rest cs))
h)))
;7096547112155234317
为什么会这样,我不知道。
我正在使用以下函数尝试创建字符串的 64 位散列,但它失败并出现 ArithmeticException,即使我使用的是 "unchecked" 版本的算术运算符。
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)
我做错了什么?
这里有个提示:
无论出于何种原因,此处函数中的第一个参数都被视为整数。添加类型提示有助于解决这个问题:
user> (reduce (fn [^long h c]
(unchecked-add (unchecked-multiply h 31) (long c)))
1125899906842597
"hello")
7096547112155234317
更新:
此外:它看起来来自 unchecked-multiply
user> (reduce (fn [h c]
(unchecked-add (unchecked-multiply ^long h 31) (long c)))
1125899906842597
"hello")
7096547112155234317
我会做一些额外的研究,并在这里更新,以防有任何新信息
更新 2: 好的,这就是我发现的:
在 https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Numbers.java 查看 clojure 的文档 我们可以看到以下内容:
我们的案例
static public Number unchecked_multiply(Object x, long y){return multiply(x,y);}
导致:
static public Number multiply(Object x, long y){
return multiply(x,(Object)y);
}
然后:
static public Number multiply(Object x, Object y){
return ops(x).combine(ops(y)).multiply((Number)x, (Number)y);
}
所以最后它从 LongOps
内部 class.
multiply
方法
final public Number multiply(Number x, Number y){
return num(Numbers.multiply(x.longValue(), y.longValue()));
}
所以最后它引导我们进行简单的(已检查?)乘法:
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;
}
轰隆隆!
所以我不知道这是错误还是预期的行为,但我觉得它真的很奇怪。
所以我唯一可以建议的是,在 clojure 中使用未经检查的数学时,请始终记住输入提示您的值。
您可以通过避免函数调用来获得您想要的行为:
(loop [h 1125899906842597
cs "hello"]
(let [c (first cs)]
(if c
(recur (unchecked-add (unchecked-multiply h 31) (long c))
(rest cs))
h)))
;7096547112155234317
为什么会这样,我不知道。