From 781af8ed94f875dda2fa56ab0d6381bde9798a73 Mon Sep 17 00:00:00 2001 From: John Aylward Date: Thu, 22 Jul 2021 23:13:19 -0400 Subject: [PATCH] correct error in converting doubles to big decimals --- src/main/java/org/json/JSONObject.java | 25 ++++++++++++++++--- .../java/org/json/junit/JSONObjectTest.java | 7 +++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index a8cfbb7ce..73d499a42 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -1159,6 +1159,18 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { * to convert. */ static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { + return objectToBigDecimal(val, defaultValue, true); + } + + /** + * @param val value to convert + * @param defaultValue default value to return is the conversion doesn't work or is null. + * @param exact When true, then {@link Double} and {@link Float} values will be converted exactly. + * When false, they will be converted to {@link String} values before converting to {@link BigDecimal}. + * @return BigDecimal conversion of the original value, or the defaultValue if unable + * to convert. + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) { if (NULL.equals(val)) { return defaultValue; } @@ -1172,7 +1184,14 @@ static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { if (!numberIsFinite((Number)val)) { return defaultValue; } - return new BigDecimal(((Number) val).doubleValue()); + if (exact) { + return new BigDecimal(((Number)val).doubleValue()); + }else { + // use the string constructor so that we maintain "nice" values for doubles and floats + // the double constructor will translate doubles to "exact" values instead of the likely + // intended representation + return new BigDecimal(val.toString()); + } } if (val instanceof Long || val instanceof Integer || val instanceof Short || val instanceof Byte){ @@ -2132,8 +2151,8 @@ static boolean isNumberSimilar(Number l, Number r) { // 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); + final BigDecimal lBigDecimal = objectToBigDecimal(l, null, false); + final BigDecimal rBigDecimal = objectToBigDecimal(r, null, false); if (lBigDecimal == null || rBigDecimal == null) { return false; } diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index cec27a9e5..eb0678de9 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -126,6 +126,7 @@ public void verifySimilar() { assertTrue("Should eval to true", obj1.similar(obj4)); + // verify that a double and big decimal are "similar" assertTrue("should eval to true",new JSONObject().put("a",1.1d).similar(new JSONObject("{\"a\":1.1}"))); } @@ -942,7 +943,7 @@ public void stringToValueNumbersTest() { assertTrue("-0 Should be a Double!",JSONObject.stringToValue("-0") instanceof Double); assertTrue("-0.0 Should be a Double!",JSONObject.stringToValue("-0.0") instanceof Double); assertTrue("'-' Should be a String!",JSONObject.stringToValue("-") instanceof String); - assertTrue( "0.2 should be a Double!", + assertTrue( "0.2 should be a BigDecimal!", JSONObject.stringToValue( "0.2" ) instanceof BigDecimal ); assertTrue( "Doubles should be BigDecimal, even when incorrectly converting floats!", JSONObject.stringToValue( new Double( "0.2f" ).toString() ) instanceof BigDecimal ); @@ -2521,8 +2522,8 @@ public void jsonObjectOptBigDecimal() { assertEquals(new BigDecimal("123"),jo.optBigDecimal("int", null)); assertEquals(new BigDecimal("654"),jo.optBigDecimal("long", null)); - assertEquals(new BigDecimal(1.234f),jo.optBigDecimal("float", null)); - assertEquals(new BigDecimal(2.345d),jo.optBigDecimal("double", null)); + assertEquals(new BigDecimal("1.234"),jo.optBigDecimal("float", null)); + assertEquals(new BigDecimal("2.345"),jo.optBigDecimal("double", null)); assertEquals(new BigDecimal("1234"),jo.optBigDecimal("bigInteger", null)); assertEquals(new BigDecimal("1234.56789"),jo.optBigDecimal("bigDecimal", null)); assertNull(jo.optBigDecimal("nullVal", null));