JS整型的坑

昨天同事跟我说js在整型长度支持上有坑,我去查了一下确实是有些坑的。js对于数值的保存遵循IEEE 754 双精度浮点格式规范,采用了双精度存储,占用64bit。如图所示:

存储格式示意图,取自wiki

  • 0到51位为尾数(也就是有效数

  • 52位到62位为指数。

    如果使用无符号整数格式,则算术中使用的指数值是偏移的指数 - 对于IEEE 754 binary64情况,指数值1023表示实际零(即2 e - 1023为1, e必须是1023)。指数范围从-1022到+1023,因为-1023(全0)和+1024(全1)的指数是为特殊数字保留的。

  • 最后一位为符号位,用来声明数值的符号,即正负。

那么可以发现,想保证结果无偏移的话只有e为1023,即有效数为 $$ 2*2^{52} = 2^{53} $$ 即js中长整型不超过

> Math.pow(2,53)
<· 9007199254740992

精度就不会丢失。坑啊(╯‵□′)╯︵┻━┻

Springboot中的处理

说一说我的处理方案吧,因为项目使用的fastjson,因此在网上看到的关于jackson的处理方式,诸如@JsonSerialize注解等方式都是不行的,使用SerializeFilter的话改动又太大,最后还是决定自己动手修改返回值类型吧。下面直接上代码:

package packagename.response;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //默认不进行拦截,返回值为false,拦截则返回true
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //对body进行处理
        if (body instanceof HashMap) {
            HashMap<String, Object> finalJson = (HashMap<String, Object>) body;
            finalJson.forEach((key, value) -> {
                if (value instanceof Long) {
                    finalJson.put(key, String.valueOf(value));
                }
            });
        }
        return body;
    }
}

如代码所示,body就是返回值内容,对body的处理就是遍历里面的value,将类型为Long的value转化为字符串再塞回去。OK,just it is.

结语

这个是这两天踩得一个小坑,重点其实不是处理部分,反而是了解了双精度的规范XD,里面关于数据偏移那部分的内容看得也是一知半解,如果内容中包含什么错误,也请路过的小伙伴给予指正,多谢~