Skip to content

Commit

Permalink
fixes issue #573 by added specific compare of numeric types
Browse files Browse the repository at this point in the history
  • Loading branch information
John J. Aylward committed Nov 19, 2020
1 parent e4b76d6 commit dec2b8b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/main/java/org/json/JSONArray.java
Expand Up @@ -1374,6 +1374,8 @@ public boolean similar(Object other) {
if (!((JSONArray)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) {
return false;
}
Expand Down
49 changes: 48 additions & 1 deletion src/main/java/org/json/JSONObject.java
Expand Up @@ -2073,6 +2073,8 @@ public boolean similar(Object other) {
if (!((JSONArray)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) {
return false;
}
Expand All @@ -2083,6 +2085,51 @@ public boolean similar(Object other) {
}
}

/**
* Compares two numbers to see if they are similar.
*
* If either of the numbers are Double or Float instances, then they are checked to have
* a finite value. If either value is not finite (NaN or ±infinity), then this
* function will always return false. If both numbers are finite, they are first checked
* to be the same type and implement {@link Comparable}. If they do, then the actual
* {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't
* implement Comparable, then they are converted to {@link String}s, then to
* {@link BigDecimal}s. Finally the 2 BigDecimal values are compared using
* {@link BigDecimal#compareTo(BigDecimal)}.
*
* @param l the Left value to compare. Can not be <code>null</code>.
* @param r the right value to compare. Can not be <code>null</code>.
* @return true if the numbers are similar, false otherwise.
*/
static boolean isNumberSimilar(Number l, Number r) {
if (!numberIsFinite(l) || !numberIsFinite(r)) {
// non-finite numbers are never similar
return false;
}

// if the classes are the same and implement Comparable
// then use the built in compare first.
if(l.getClass().equals(r.getClass()) && l instanceof Comparable) {
@SuppressWarnings({ "rawtypes", "unchecked" })
int compareTo = ((Comparable)l).compareTo(r);
return compareTo==0;
}

// BigDecimal should be able to handle all of our number types that we support through
// documentation. Convert to BigDecimal first, then use the Compare method to
// decide equality.
return new BigDecimal(l.toString()).compareTo(new BigDecimal(r.toString())) == 0;
}

private static boolean numberIsFinite(Number n) {
if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) {
return false;
} else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) {
return false;
}
return true;
}

/**
* Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
*
Expand Down Expand Up @@ -2354,7 +2401,7 @@ public static String valueToString(Object value) throws JSONException {
*/
public static Object wrap(Object object) {
try {
if (object == null) {
if (NULL.equals(object)) {
return NULL;
}
if (object instanceof JSONObject || object instanceof JSONArray
Expand Down

0 comments on commit dec2b8b

Please sign in to comment.