diff --git a/src/main/java/org/json/JSONArray.java b/src/main/java/org/json/JSONArray.java
index f11b328d8..338177c59 100644
--- a/src/main/java/org/json/JSONArray.java
+++ b/src/main/java/org/json/JSONArray.java
@@ -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;
}
diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java
index f718c0618..2d870b0c8 100644
--- a/src/main/java/org/json/JSONObject.java
+++ b/src/main/java/org/json/JSONObject.java
@@ -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;
}
@@ -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 null
.
+ * @param r the right value to compare. Can not be null
.
+ * @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.
*
@@ -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