From 0b4b996b8b6a9fbe2590d39fc9ee08875b393def Mon Sep 17 00:00:00 2001 From: rowstop Date: Thu, 24 Oct 2024 14:28:23 +0800 Subject: [PATCH] When using BrowserCompatible, BigDecimal in the Double range behaves the same as Double. for issue #3123 --- .../com/alibaba/fastjson2/JSONWriter.java | 3 +- .../com/alibaba/fastjson2/util/TypeUtils.java | 19 +++++++- .../fastjson2/issues_3100/Issue3123.java | 44 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3123.java diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java index e9ce24ad68..7770338485 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java @@ -853,8 +853,7 @@ protected static boolean isWriteAsString(BigDecimal value, long features) { } return (features & BrowserCompatible.mask) != 0 - && value.precision() >= 16 - && !isJavaScriptSupport(value.unscaledValue()); + && !isJavaScriptSupport(value); } public abstract void writeNameRaw(char[] chars); diff --git a/core/src/main/java/com/alibaba/fastjson2/util/TypeUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/TypeUtils.java index 1362d53bbc..73ed49f25f 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/TypeUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/TypeUtils.java @@ -4343,8 +4343,23 @@ public static boolean isJavaScriptSupport(long i) { return i >= LONG_JAVASCRIPT_LOW && i <= LONG_JAVASCRIPT_HIGH; } - public static boolean isJavaScriptSupport(BigDecimal i) { - return i.precision() >= 16 && isJavaScriptSupport(i.unscaledValue()); + public static boolean isJavaScriptSupport(BigDecimal decimal) { + boolean jsSupport = decimal.precision() < 16 || isJavaScriptSupport(decimal.unscaledValue()); + if (!jsSupport && decimal.scale() != 0) { + //Use double for comparison + //double and javascript number have the same precision + //In extreme cases, precision loss may occur. + //There will be a loss of precision between [4.9e-324, 5e-324), which will be converted to 5e-324 by JavaScript. + //This situation can be ignored + double doubleValue; + try { + doubleValue = decimal.doubleValue(); + } catch (Exception ex) { + return false; + } + jsSupport = decimal.compareTo(BigDecimal.valueOf(doubleValue)) == 0; + } + return jsSupport; } public static boolean isJavaScriptSupport(BigInteger i) { diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3123.java b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3123.java new file mode 100644 index 0000000000..e1790ec752 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3100/Issue3123.java @@ -0,0 +1,44 @@ +package com.alibaba.fastjson2.issues_3100; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +/** + * @author 张治保 + * @since 2024/10/22 + */ +public class Issue3123 { + @Test + @SneakyThrows + void test() { + BigDecimal num1 = BigDecimal.valueOf(107.29305145106407); + BigDecimal num2 = BigDecimal.valueOf(23.42835970945431); + BigDecimal num3 = BigDecimal.valueOf(23.427479896686418); + + Double num11 = 107.29305145106407; + Double num21 = 23.42835970945431; + Double num31 = 23.427479896686418; + + Map map = new HashMap<>(); + map.put("b1", num1); + map.put("b2", num2); + map.put("b3", num3); + + map.put("d1", num11); + map.put("d2", num21); + map.put("d3", num31); + + JSONAssert.assertEquals( + "{\"b1\":107.29305145106407,\"b2\":23.42835970945431,\"b3\":23.427479896686418,\"d1\":107.29305145106407,\"d2\":23.42835970945431,\"d3\":23.427479896686418}", + JSON.toJSONString(map, JSONWriter.Feature.BrowserCompatible), + true + ); + } +}