美文网首页
Java返回前端长整形精度问题

Java返回前端长整形精度问题

作者: 骑着乌龟去看海 | 来源:发表于2018-06-23 10:19 被阅读187次
1. 问题描述

最近在给前端返回Long类型的数据时,发现返回的数据到前端的时候,精度发生了变化:

后台返回:221629798350123011
前端接收:221629798350123000

也就是说,最终前端接收的时候,这个值不是一个精确的值。大致了解了下,原因大致如下:

2. 原因

  JavaScript中表示数值的只有一个类型,就是Number,Number类型采用IEEE754标准来表示数字,不区分整数和浮点数,统一使用64bit 浮点数来进行表示。而在一段范围之内,所有的整数都有唯一的浮点数表示,这些整数叫做安全整数,而在JS里,安全整数的范围是:

Number.MAX_SAFE_INTEGER  =   2^53 - 1        => 9007199254740991
Number.MIN_SAFE_INTEGER   = - (2^53 - 1)     => -9007199254740991

在安全整数范围之内的整形都能够正常显示,而超出安全整数范围的整数则都是不可靠的。
根据国际标准IEEE 754,JS浮点数的64个二进制位,从最左边开始,依次是:

第1位:            符号位,0正数,1表示负数
第2位到第12位:    储存指数部分
第13位到第64位:   储存小数部分(即有效数字)

  其中,IEEE 754规定,有效数字第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字总是1.xx...xx的形式,因此,JavaScript提供的有效数字最长为53个二进制位,意味着绝对值小于2的53次方的整数,即-(253-1) 到 253-1 都可以精确表示。所以换算成十进制的话,JS数字的最高精度是16位,而上面我们传递的那个数值,很明显超过了16位,所以这个结果是不精确的。

3. 解决方式

  解决方式也很简单了,就是返回的时候不用数值类型,使用字符串的形式进行返回。而在后台的处理中,有多种方式方式将Long类型转为字符串类型,这里我们了解两种常用的方式。

  1. Spring MVC中默认使用的json解析工具是Jackson,使用的默认Message转换器是Jackson的MappingJackson2HttpMessageConverter,实际实现是通过该对象的ObjectMapper来实现的,所以我们可以修改一下ObjectMapper的一些设置:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
            new MappingJackson2HttpMessageConverter();
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    objectMapper.registerModule(simpleModule);
    jackson2HttpMessageConverter.setObjectMapper(objectMapper);
    converters.add(jackson2HttpMessageConverter);
    converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
}
  1. 上面这种方式是针对全局实现的,会将全局内所有的Long对象转换为字符串类型, 有的时候我们可能只想针对特定类中的某个字段,那么这时候我们可以在实体中,通过配置Jackson的JsonIncludeJsonSerialize来实现:
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
public class User implements Serializable{
    private String name;
    private String id;
    private Double aDouble;
    @JsonSerialize(using = ToStringSerializer.class)
    private Long sum;
    
    // set,get省略
}
  1. 现在我们工程中经常会用到fastjson,如果我们想通过使用fastjson来实现该类型转换的话,可以通过如下配置:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    SerializeConfig serializeConfig = SerializeConfig.globalInstance;
    serializeConfig.put(Long.class, ToStringSerializer.instance);
    fastJsonConfig.setSerializeConfig(serializeConfig);
    fastConverter.setFastJsonConfig(fastJsonConfig);
    converters.add(fastConverter);
}

这里fastjson版本:1.2.47,版本不同,代码可能会稍有不同。

  1. 同样,如果我们不想针对全局做转换,如果要针对单个实现类,可以通过fastjson的JSONField来实现(记得先将Jackson转为Fastjson):
public class User implements Serializable{
    private String name;
    private String id;
    private Double aDouble;
    @JSONField(serializeUsing= ToStringSerializer.class)
    private Long sum;
}

Fastjson在com.alibaba.fastjson.serializer包下面提供了多种数据类型转换的注解,我们可以有选择的使用,如果有需要,也可以实现ObjectSerializer来自定义我们的实现。

本文参考自:JavaScript 类型系统——Number 数字类型

相关文章

网友评论

      本文标题:Java返回前端长整形精度问题

      本文链接:https://www.haomeiwen.com/subject/azdryftx.html