Details

Type: Defect

Status: Closed

Priority: Major

Resolution: Completed

Affects Version/s: Release 1.4, Release 1.5

Fix Version/s: Release 1.6

Component/s: None

Labels:

Patch:Code and Test

Approval:Ok
Description
BigDecimal does not have consistent comparison semantics with other numeric types.
user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 user> (== 1.0M 1.00M) false ;; potentially surprising
Patch: clj1118v7.patch
Approach: Change equiv for BigDecimals so that instead of using BigDecimal.equals(), it uses BigDecimal.compareTo() and checks the return value is equal to 0.
The javadoc for these methods explicitly states 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.
Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies hasheq() to return the same value for all numerically equal BigDecimal values, by calling BigDecimal.stripTrailingZeros() on them before hashing. This change also will make 1.0M == 1.00M which was not true before.
Screened by: Alex Miller
Attachments
Activity
Field  Original Value  New Value 

Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv1.txt [ 11957 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv2.txt [ 11959 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv1.txt [ 11957 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv3.txt [ 11960 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv2.txt [ 11959 ] 
Patch  Code and Test [ 10002 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv4.txt [ 12192 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv3.txt [ 11960 ] 
Labels  math  
Description 
user> *clojureversion*
{: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? 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? 
Description 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? Part of a message about this issue from the following thread is copied below: https://mail.google.com/mail/u/0/?shva=1#label/clojure/1403d494668229d8 It'd be great if this got fixed  we met an ugly bug yesterday due to this on our project. Our system validates that the sum of two monetary fields A and B equals the sum of two monetary fields C and D. We parse the fields via Cheshire with conversion to BigdDecimal turned on  but any fields with no fractional part get parsed as Integers anyway. So if A=$1.50, B=$1.50, C=$2 and D=$1, (== (+ A B) (+ C D)) is false. 
Approval  Triaged [ 10120 ] 
Fix Version/s  Release 1.6 [ 10157 ]  
Approval  Triaged [ 10120 ]  Vetted [ 10003 ] 
Description 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? Part of a message about this issue from the following thread is copied below: https://mail.google.com/mail/u/0/?shva=1#label/clojure/1403d494668229d8 It'd be great if this got fixed  we met an ugly bug yesterday due to this on our project. Our system validates that the sum of two monetary fields A and B equals the sum of two monetary fields C and D. We parse the fields via Cheshire with conversion to BigdDecimal turned on  but any fields with no fractional part get parsed as Integers anyway. So if A=$1.50, B=$1.50, C=$2 and D=$1, (== (+ A B) (+ C D)) is false. 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? Part of a message about this issue from the following thread is copied below: https://mail.google.com/mail/u/0/?shva=1#label/clojure/1403d494668229d8 It'd be great if this got fixed  we met an ugly bug yesterday due to this on our project. Our system validates that the sum of two monetary fields A and B equals the sum of two monetary fields C and D. We parse the fields via Cheshire with conversion to BigdDecimal turned on  but any fields with no fractional part get parsed as Integers anyway. So if A=$1.50, B=$1.50, C=$2 and D=$1, (== (+ A B) (+ C D)) is false. *Patch*: clj1118makedoubleequalstrueformorebigdecimalspatchv4.txt *Approach*: Change 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies hasheq() to return the same value for all numerically equal BigDecimal values, by calling BigDecimal.stripTrailingZeros() on them before hashing. 
Assignee  Alex Miller [ alexmiller ] 
Approval  Vetted [ 10003 ]  Incomplete [ 10006 ] 
Assignee  Alex Miller [ alexmiller ] 
Assignee  Alex Miller [ alexmiller ]  
Approval  Incomplete [ 10006 ]  Vetted [ 10003 ] 
Attachment  clj1118makedoubleequalstrueformorebigdecimalspatchv5.txt [ 12283 ]  
Description 
{code}
user> *clojureversion* {: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 {code} It's not clear if this is a bug or an enhancement request, Should BigDecimal's be special in comparason to their smaller equivalents? Part of a message about this issue from the following thread is copied below: https://mail.google.com/mail/u/0/?shva=1#label/clojure/1403d494668229d8 It'd be great if this got fixed  we met an ugly bug yesterday due to this on our project. Our system validates that the sum of two monetary fields A and B equals the sum of two monetary fields C and D. We parse the fields via Cheshire with conversion to BigdDecimal turned on  but any fields with no fractional part get parsed as Integers anyway. So if A=$1.50, B=$1.50, C=$2 and D=$1, (== (+ A B) (+ C D)) is false. *Patch*: clj1118makedoubleequalstrueformorebigdecimalspatchv4.txt *Approach*: Change 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies hasheq() to return the same value for all numerically equal BigDecimal values, by calling BigDecimal.stripTrailingZeros() on them before hashing. 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118makedoubleequalstrueformorebigdecimalspatchv5.txt *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. *Screened by:* Alex Miller 
Approval  Vetted [ 10003 ]  Screened [ 10004 ] 
Attachment  clj1118v6.txt [ 12284 ] 
Approval  Screened [ 10004 ]  Incomplete [ 10006 ] 
Attachment  clj1118v7.patch [ 12357 ]  
Description 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118makedoubleequalstrueformorebigdecimalspatchv5.txt *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. *Screened by:* Alex Miller 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118v7.patch *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. *Screened by:* Alex Miller 
Approval  Incomplete [ 10006 ]  Screened [ 10004 ] 
Assignee  Alex Miller [ alexmiller ] 
Description 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118v7.patch *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. *Screened by:* Alex Miller 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118v7.patch *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. This change also will make 1.0M == 1.00M which was not true before. *Screened by:* Alex Miller 
Description 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 {code} *Patch*: clj1118v7.patch *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. This change also will make 1.0M == 1.00M which was not true before. *Screened by:* Alex Miller 
BigDecimal does not have consistent comparison semantics with other numeric types.
{code} user> *clojureversion* {:major 1, :minor 5, :incremental 1, :qualifier nil} 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 user> (== 1.0M 1.00M) false ;; potentially surprising {code} *Patch*: clj1118v7.patch *Approach*: Change equiv for BigDecimals so that instead of using {{BigDecimal.equals()}}, it uses {{BigDecimal.compareTo()}} and checks the return value is equal to 0. The javadoc for these methods explicitly states 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. Doing this also changes the behavior of = when comparing BigDecimal values to other numbers. hash should be consistent with =, so now hash should return the same value for all numerically equal BigDecimal values. This patch modifies {{hasheq()}} to return the same value for all numerically equal BigDecimal values, by calling {{BigDecimal.stripTrailingZeros()}} on them before hashing. This change also will make 1.0M == 1.00M which was not true before. *Screened by:* Alex Miller 
Approval  Screened [ 10004 ]  Ok [ 10007 ] 
Status  Open [ 1 ]  Closed [ 6 ] 
Resolution  Completed [ 1 ] 
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.