Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

谈谈node大整数 #1

Open
wangchao0502 opened this issue Mar 4, 2017 · 0 comments
Open

谈谈node大整数 #1

wangchao0502 opened this issue Mar 4, 2017 · 0 comments

Comments

@wangchao0502
Copy link
Owner

wangchao0502 commented Mar 4, 2017

缘起

公司组织架构大规模调整,需要同步oa系统的架构到腾讯邮箱(后用exmail代替)上。为了减少手工劳作并造福后人,决定封装exmail的api并告成命令行工具。由于oa系统就是node全栈开发的,理所当然就用node来完成这个同步的工作。但是工作中遇到了奇怪的问题。
exmail的部门id大概有这么长40066889765020778,基本上可以确定对应数据库的bigint类型,为什么不是字符串?因为返回的json数据是不带引号的~这时出现了神奇的情况。

{
    "id": 5196969938080914000,
    "name": "集团运营_2017",
    "parentid": 26556090882909804,
    "order": 0 
}, {
    "id": 5196969938080914000,
    "name": "垂直行业_2017",
    "parentid": 26556090882909804,
    "order": 0
}, {
    "id": 5196969938080914000,
    "name": "电商事业群_2017",
    "parentid": 26556090882909804,
    "order": 0
} 

看到了吗,id是一样的,这科学吗?显然不科学。思考3秒钟,以为是exmail接口的bug,于是问了他们的开发人员,他们说是不是整数越界引起的。我突然意识到很有道理呀,为什么不再多思考几秒(叹气)?
于是我直接打印出了http请求response的text,因为http通讯返回的都是字符串,转换成json的过程是应用层做的,也就是调用了JSON.parse方法。真相如下:

'{"id":5196969938080914114,"name":"集团运营_2017","parentid":26556090882909805,"order":0},{96969938080914125,"name":"垂直行业_2017","parentid":26556090882909805,"order":0},{"id":5196969938080914132,"name":"电商事业群_2017","parentid":26556090882909805,"order":0}'

所以问题就出在js处理大整数的这个过程

分析

思考了一下node的number类型,虽然没有明显区分整数与浮点数,但v8肯定是有做优化的,否则计算效率不要慢死。再一想number类型是支持64位的,也就是无论整数还是浮点数都是64bit存储,既然node(javascript)语法层面没有显性定义整数和浮点数类型,那么整数的底层存储结构应该同浮点数一致。也就是大学计算机组成原理学过的:

number = (-1)^sign*M^E
javascript的浮点数遵守IEEE754规范。既然底数为52位,那么正整数的安全上界也很容易计算,即2^53-1。因为52个比特位全部是1表示的整数就是2^53-1。而这个数值其实在js中可以查到,为Number.MAX_SAFE_INTEGER。也就是小于等于这个整数是可以安全使用的,那么大于它会出现什么意外呢?

> Number.MAX_SAFE_INTEGER
9007199254740991
> Number.MAX_SAFE_INTEGER+1
9007199254740992
> Number.MAX_SAFE_INTEGER+2
9007199254740992

我们发现+1和+2的结果是相同的,为什么呢?

解决

想到几种方案

  • 避开JSON.parse的操作,也就是调用原生的http.post方法,通过自己构造body字符串的方式发起请求。返回结果也需要自己动手解析。将bigint用字符串保存。
  • 使用node-bigint node-int64这种支持大整数的包,底层使用c++ addon。但仍然需要自己去解析response的text。
  • 搞一个支持大整数的node版本?
  • 使用支持64位整数的语言(Java, python, ruby, go...)

最终,我用go完成了这个任务

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant