From bbe833f2c77b6f9ec9d6ae2e0caa5c9adeb4b507 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Fri, 27 Sep 2013 14:47:27 -0500 Subject: [PATCH] CLJ-1118: Make == true for BigDecimals that differ only in scale --- src/jvm/clojure/lang/Numbers.java | 17 +++++++++++++- test/clojure/test_clojure/numbers.clj | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java index a50f287..0a3e09b 100644 --- a/src/jvm/clojure/lang/Numbers.java +++ b/src/jvm/clojure/lang/Numbers.java @@ -898,7 +898,7 @@ final static class BigDecimalOps extends OpsP{ } public boolean equiv(Number x, Number y){ - return toBigDecimal(x).equals(toBigDecimal(y)); + return toBigDecimal(x).compareTo(toBigDecimal(y)) == 0; } public boolean lt(Number x, Number y){ @@ -972,6 +972,21 @@ static int hasheq(Number x){ long lpart = x.longValue(); return (int) (lpart ^ (lpart >>> 32)); } + if(xc == BigDecimal.class) + { + // stripTrailingZeros() to make all numerically equal + // BigDecimal values come out the same before calling + // hashCode. Special check for 0 because + // stripTrailingZeros() does not do anything to values + // equal to 0 with different scales. + if (isZero(x)) + return BigDecimal.ZERO.hashCode(); + else + { + BigDecimal tmp = ((BigDecimal) x).stripTrailingZeros(); + return tmp.hashCode(); + } + } return x.hashCode(); } diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj index f886f4f..0a3dac5 100644 --- a/test/clojure/test_clojure/numbers.clj +++ b/test/clojure/test_clojure/numbers.clj @@ -50,6 +50,49 @@ (deftest BigInteger-conversions (not (decimal? v)) (not (float? v)))))) +(defn all-pairs-equal [equal-var vals] + (doseq [val1 vals] + (doseq [val2 vals] + (is (equal-var val1 val2) + (str "Test that " val1 " (" (class val1) ") " + equal-var " " val2 " (" (class val2) ")"))))) + +(defn all-pairs-hash-consistent-with-= [vals] + (doseq [val1 vals] + (doseq [val2 vals] + (when (= val1 val2) + (is (= (hash val1) (hash val2)) + (str "Test that (hash " val1 ") (" (class val1) ") " + " = (hash " val2 ") (" (class val2) ")")))))) + +(deftest equality-tests + ;; = only returns true for numbers that are in the same category, + ;; where category is one of INTEGER, FLOATING, DECIMAL, RATIO. + (all-pairs-equal #'= [(byte 2) (short 2) (int 2) (long 2) + (bigint 2) (biginteger 2)]) + (all-pairs-equal #'= [(float 2.0) (double 2.0)]) + (all-pairs-equal #'= [2.0M 2.00M]) + (all-pairs-equal #'= [(float 1.5) (double 1.5)]) + (all-pairs-equal #'= [1.50M 1.500M]) + (all-pairs-equal #'= [0.0M 0.00M]) + (all-pairs-equal #'= [(/ 1 2) (/ 2 4)]) + + (all-pairs-hash-consistent-with-= [(byte 2) (short 2) (int 2) (long 2) + (bigint 2) (biginteger 2) + (float 2.0) (double 2.0) 2.0M 2.00M]) + (all-pairs-hash-consistent-with-= [(/ 3 2) (float 1.5) 1.50M 1.500M]) + (all-pairs-hash-consistent-with-= [(float 0.0) 0.0M 0.00M]) + + ;; == tests for numerical equality, returning true even for numbers + ;; in different categories. + (all-pairs-equal #'== [(byte 0) (short 0) (int 0) (long 0) + (bigint 0) (biginteger 0) + (float 0.0) (double 0.0) 0.0M 0.00M]) + (all-pairs-equal #'== [(byte 2) (short 2) (int 2) (long 2) + (bigint 2) (biginteger 2) + (float 2.0) (double 2.0) 2.0M 2.00M]) + (all-pairs-equal #'== [(/ 3 2) (float 1.5) (double 1.5) 1.50M 1.500M])) + (deftest unchecked-cast-num-obj (do-template [prim-array cast] (are [n] -- 1.8.3.2