为什么Java BigDecimal调用stripTrailingZeros后会输出科学计数法?
BigDecimal stripTrailingZeros后toString()输出科学计数法的原因及解决方案
现象复现
你遇到的输出差异是Java BigDecimal的标准行为,测试代码如下:
public static void main(String[] asd) { // prints: "with stripTrailingZeros: 5E+1" System.out.println("with stripTrailingZeros: " + new BigDecimal("50").stripTrailingZeros()); // prints: "without stripTrailingZeros: 50" System.out.println("without stripTrailingZeros: " + new BigDecimal("50").toString()); }
底层原因
这个行为是BigDecimal的两个设计规则共同导致的:
- stripTrailingZeros的作用不止是删除末尾的零,还会同步调整BigDecimal的精度位(scale)和无标度值(unscaledValue):
- 未调用方法的
new BigDecimal("50")参数为:unscaledValue = 50,scale = 0 - 调用stripTrailingZeros后参数变为:unscaledValue = 5,scale = -1
- 未调用方法的
- toString()方法的输出规则为:当scale为负数时,自动使用科学计数法输出,以保证输出的字符串长度最短,同时准确体现当前BigDecimal的精度状态。Java官方并没有保证toString()的输出是人类可读的普通数值格式,它的设计目标是能通过
new BigDecimal(str)准确还原当前BigDecimal对象。
解决方案
针对JSON序列化返回值不一致的问题,全局配置Jackson的序列化规则是成本最低的通用方案,不存在不合理的问题,只需要几行代码就能全局生效,不需要对业务代码做任何修改:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfiguration { @Bean public ObjectMapper customObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); // 全局启用BigDecimal输出为普通字符串,不使用科学计数法 objectMapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN); return objectMapper; } }
如果只需要部分字段使用普通格式输出,不需要全局修改,可以在对应字段上添加序列化注解:
@JsonSerialize(using = ToStringSerializer.class) private BigDecimal amount;
内容的提问来源于stack exchange,提问作者gshock
相关产品推荐
相关产品推荐

