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)

どちらも同じように振る舞うべきではありませんか?2 つのMAX_VALUE値を加算するとオーバーフローするのに、1 を加算するとオーバーフローしないのはなぜですか?

私はこのSOの質問を見てきましたが、彼らは私とは異なる振る舞いをしています。

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.4.0 ではなく、Clojure 1.2.x でテストを行ったと思います。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 のように動作するいくつかの ops のバージョンがあります。

(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 に答える