fixes issue #573 by added specific compare of numeric types

This commit is contained in:
John J. Aylward 2020-11-19 18:24:56 -05:00
parent e4b76d6588
commit 11e6b1af7e
2 changed files with 54 additions and 1 deletions

View File

@ -1374,6 +1374,8 @@ public class JSONArray implements Iterable<Object> {
if (!((JSONArray)valueThis).similar(valueOther)) { if (!((JSONArray)valueThis).similar(valueOther)) {
return false; return false;
} }
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) { } else if (!valueThis.equals(valueOther)) {
return false; return false;
} }

View File

@ -2073,6 +2073,8 @@ public class JSONObject {
if (!((JSONArray)valueThis).similar(valueOther)) { if (!((JSONArray)valueThis).similar(valueOther)) {
return false; return false;
} }
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) { } else if (!valueThis.equals(valueOther)) {
return false; return false;
} }
@ -2083,6 +2085,55 @@ public class JSONObject {
} }
} }
/**
* 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 &#177;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 BigDecimal}s. Finally the
* 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.
final BigDecimal lBigDecimal = objectToBigDecimal(l, null);
final BigDecimal rBigDecimal = objectToBigDecimal(r, null);
if (lBigDecimal == null || rBigDecimal == null) {
return false;
}
return lBigDecimal.compareTo(rBigDecimal) == 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. * Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
* *
@ -2354,7 +2405,7 @@ public class JSONObject {
*/ */
public static Object wrap(Object object) { public static Object wrap(Object object) {
try { try {
if (object == null) { if (NULL.equals(object)) {
return NULL; return NULL;
} }
if (object instanceof JSONObject || object instanceof JSONArray if (object instanceof JSONObject || object instanceof JSONArray