[ 
https://issues.apache.org/jira/browse/MATH-1690?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Alex Herbert resolved MATH-1690.
--------------------------------
    Fix Version/s: 4.0-beta2
       Resolution: Fixed

Documented in commit:
 
f0f0af1353f30dce0b6e0e4607960f4164328c6e
 

> SparseGradient.equals() uses ULP-based comparison but hashCode() uses exact 
> doubles
> -----------------------------------------------------------------------------------
>
>                 Key: MATH-1690
>                 URL: https://issues.apache.org/jira/browse/MATH-1690
>             Project: Commons Math
>          Issue Type: Improvement
>          Components: legacy
>            Reporter: Ruiqi Dong
>            Priority: Minor
>             Fix For: 4.0-beta2
>
>
> *Summary*
> SparseGradient.equals(Object) treats values within 1 ULP as equal, but 
> hashCode() hashes the exact double values. This breaks the Java contract that 
> equal objects must have equal hash codes.
>  
> *Affected code*
> File: 
> commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/analysis/differentiation/SparseGradient.java
> {code:java}
> if (other instanceof SparseGradient) {
>     final SparseGradient rhs = (SparseGradient)other;
>     if (!Precision.equals(value, rhs.value, 1)) {
>         return false;
>     }
>     ...
>     if (!Precision.equals(entry.getValue(), 
> rhs.derivatives.get(entry.getKey()), 1)) {
>         return false;
>     }
>     return true;
> }
> @Override
> public int hashCode() {
>     return 743 + 809 * Double.hashCode(value) + 167 * derivatives.hashCode();
> } {code}
> *Reproducer*
> Add the following test to 
> commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/differentiation/SparseGradientTest.java:
> {code:java}
> @Test
> public void testHashCodeMatchesEqualsForUlpAdjacentValues() {
>     final SparseGradient constantA = SparseGradient.createConstant(1.0);
>     final SparseGradient constantB = 
> SparseGradient.createConstant(JdkMath.nextUp(1.0));
>     Assert.assertEquals(constantA, constantB);
>     Assert.assertEquals(constantA.hashCode(), constantB.hashCode());
>     final SparseGradient derivativeA = SparseGradient.createVariable(0, 
> 0.0).multiply(1.0);
>     final SparseGradient derivativeB =
>             SparseGradient.createVariable(0, 
> 0.0).multiply(JdkMath.nextUp(1.0));
>     Assert.assertEquals(derivativeA, derivativeB);
>     Assert.assertEquals(derivativeA.hashCode(), derivativeB.hashCode());
> } {code}
> Run:
> {code:java}
> mvn -q -pl commons-math-legacy 
> -Dtest=org.apache.commons.math4.legacy.analysis.differentiation.SparseGradientTest
>  test {code}
> Observed behavior:
> {code:java}
> SparseGradientTest.testHashCodeMatchesEqualsForUlpAdjacentValues:60
> expected:<225444583> but was:<225445392> {code}
> Expected behavior:
> If two SparseGradient instances are equal according to equals(), they need to 
> return the same hash code.
>  
> The current implementation mixes approximate equality with exact hashing, 
> which is a direct equals/hashCode contract violation.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to