7

我正在运行 Clojure 1.4.0。为什么如果我添加Integer/MAX_VALUE和 1,我会得到一个 Long,但如果我添加Integer/MAX_VALUE到自身,我会得到一个异常?

=> (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)

他们不应该以同样的方式行事吗?为什么添加两个MAX_VALUE值会溢出但添加 1 不会?

我已经看到了这个 SO question,但他们的行为与我不同。

4

3 回答 3

7

这很奇怪,我在 Ubuntu 12.04 64 位上使用 Clojure 1.4.0 和 Java(TM) SE 运行时环境(构建 1.7.0_06-b24)看到了不同的结果:

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

您始终可以检查clojure.core 用于 numerics 的 Java 类,以了解该功能是如何实现的:

+ 运算符的实现在:

(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)))

添加long的Java实现:

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

编辑:使用 Clojure 1.2.1 进行测试
我已经使用 Clojure 1.2.1 进行了快速测试,并且使用该版本的 Clojure 我完全了解您的行为。

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)

我会说您使用 Clojure 1.2.x 进行了测试,而不是使用 1.4.0。你的 REPL 中 *clojure-version* 的值是多少?

于 2012-08-19T18:36:30.570 回答
4

看起来你有你的答案,但这里有一些其他有趣的点:

java(所有版本)和clojure(> 1.3.0)的默认行为在溢出时的行为不同

在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 

这是因为算术在 jvm 上默认包装

在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 确实有一些行为类似于 java 的操作的版本

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

您可以通过设置*unchecked-math*为 true将未选中的操作设为默认值

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

还有很多其他有趣的(unchecked-*)操作。

于 2012-08-19T21:17:44.080 回答
1

从 1.3.0 版开始,Clojure 对所有原始数字都使用 Long。你只需要使用更大的数字来获得溢出。

 (def max-plus-one (+ Long/MAX_VALUE one))
于 2012-08-19T18:24:17.020 回答