Clojure

inconsistent numeric comparison semantics between BigDecimal and other Numerics

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: Release 1.4, Release 1.5
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code and Test

Description

user> clojure-version
{:major 1, :minor 5, :incremental 0, :qualifier "beta1"}
user> (== 2.0 2.0M)
true
user> (== 2 2.0M)
false <-- this one is not like the others
user> (== 2 2.0)
true
user> (== 2N 2.0)
true
user> (== 2 (double 2.0M))
true

It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents?

Activity

Hide
Arthur Ulfeldt added a comment -

I understand that the definition of equality between bigDecimals is dependent on both value and scale as in this case:

user> (== 0.000000M 0.0M)
false

I just want to make sure the decission to propagate that semantic across types is intentional. If this is on purpose than this is not a bug.

Show
Arthur Ulfeldt added a comment - I understand that the definition of equality between bigDecimals is dependent on both value and scale as in this case: user> (== 0.000000M 0.0M) false I just want to make sure the decission to propagate that semantic across types is intentional. If this is on purpose than this is not a bug.
Hide
Arthur Ulfeldt added a comment - - edited

this could be fixed by calling stripTrailingZeros on bigDecimals before comparing them to Longs or BigInts.

(== 2 (double (. 2.0M stripTrailingZeros)))
true

Edited by Andy Fingerhut: Unfortunately that fails for BigDecimal values equal to 0, unless they happen to have a scale that matches what you are comparing it to.

I think a more complete solution is to use BigDecimal's compareTo method, e.g.:

(zero? (.compareTo 2.0M (bigdec 2)))
true

Show
Arthur Ulfeldt added a comment - - edited this could be fixed by calling stripTrailingZeros on bigDecimals before comparing them to Longs or BigInts. (== 2 (double (. 2.0M stripTrailingZeros))) true Edited by Andy Fingerhut: Unfortunately that fails for BigDecimal values equal to 0, unless they happen to have a scale that matches what you are comparing it to. I think a more complete solution is to use BigDecimal's compareTo method, e.g.: (zero? (.compareTo 2.0M (bigdec 2))) true
Hide
Timothy Baldridge added a comment -

It seems we need some more eyes on this issue, can you bring this up on clojure-dev and see what they think?

Show
Timothy Baldridge added a comment - It seems we need some more eyes on this issue, can you bring this up on clojure-dev and see what they think?
Hide
Andy Fingerhut added a comment -

Patch clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v1.txt dated Apr 14 2013 changes equiv for BigDecimals so that instead of using BigDecimal.equals(), it uses BigDecimal.compareTo() and checks the return value is equal to 0.

The Java docs for these methods explicitly state that BigDecimal.equals() will treat values that are otherwise equal numerically, but differ in scale, as not equal.

They also say that BigDecimal.compareTo() will return 0 for such BigDecimals.

I'm not sure if this is the preferred behavior for Clojure, but if it is, this patch should do it.

Show
Andy Fingerhut added a comment - Patch clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v1.txt dated Apr 14 2013 changes equiv for BigDecimals so that instead of using BigDecimal.equals(), it uses BigDecimal.compareTo() and checks the return value is equal to 0. The Java docs for these methods explicitly state that BigDecimal.equals() will treat values that are otherwise equal numerically, but differ in scale, as not equal. They also say that BigDecimal.compareTo() will return 0 for such BigDecimals. I'm not sure if this is the preferred behavior for Clojure, but if it is, this patch should do it.
Hide
Andy Fingerhut added a comment -

clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v2.txt dated Apr 14 2013 is same as clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v1.txt described in previous comment, except it also has some new tests included.

Show
Andy Fingerhut added a comment - clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v2.txt dated Apr 14 2013 is same as clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v1.txt described in previous comment, except it also has some new tests included.
Hide
Andy Fingerhut added a comment -

clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v3.txt dated Apr 15 2013 is the same as the the previous patch clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v2.txt, except for the following:

By changing == behavior for BigDecimal by modifying the BigDecimalOps.equiv() method, that also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return same value for all numerically equal BigDecimal values. This patch should achieve that.

Show
Andy Fingerhut added a comment - clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v3.txt dated Apr 15 2013 is the same as the the previous patch clj-1118-make-double-equals-true-for-more-bigdecimals-patch-v2.txt, except for the following: By changing == behavior for BigDecimal by modifying the BigDecimalOps.equiv() method, that also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return same value for all numerically equal BigDecimal values. This patch should achieve that.

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: