给前端返回long类型造成精度损失
2022年12月4日 · 670 字
今天同事遇到一个问题,后端给前端返回一个long类型的参数列表,前端使用
原因
JS的基础类型Number,遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。如图

意义
- 1位用来表示符号位
- 11位用来表示指数
- 52位表示尾数
浮点数,比如
`0.1 >> 0.0001 1001 1001 1001…(1001无限循环)``0.2 >> 0.0011 0011 0011 0011…(0011无限循环)`
此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。
大于 9007199254740992 的可能会丢失精度
解决办法
1、使用ToStringSerializer的注解,让系统序列化
时,保留相关精度
@JsonSerialize(using=ToStringSerializer.class)
private Long createdBy;
FastJson 注解
@JSONField(serializeUsing= ToStringSerializer.class)
上述方法需要在每个对象都配上该注解,此方法过于繁锁。
2、使用全局配置,将转换时实现自动ToStringSerializer序列化
Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
/**
* 序列换成json时,将所有的long变成string
* 因为js中得数字类型不能包含所有的java long值
*/
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
项目中很多时候都会用到json,常用的有fastjson,Jackson等等这些,有时候为了统一,我们通常就会约定使用某一种。当然,有时候项目中也可能会统一约定使用了fastjson,然而Spring MVC中默认是使用了Jackson的
在Spring Boot中将Jackson替换为fastjson一般会有两种方式:
第一种:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverter() {
return new HttpMessageConverters(new FastJsonHttpMessageConverter());
}
}
第二种:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter =
new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
}
替换成fastjson之后,对于精度丢失问题,我们可以这么去做:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter =
new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
fastJsonConfig.setSerializeConfig(serializeConfig);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
}
总结
吃一堑长一智,需要精度准确的比如浮点数或者长整形最好给前端返回string类型