好几年前学 javascript 的时候,网上都说 js 内部数字表达方式就一种,用浮点来表示。
然而我看了 google 的代码后,对这个观点,表示强烈怀疑。
这是 google 的代码库:https://github.com/google/closure-library/blob/master/closure/goog/math/long.js
开头明确写着是 32 位整型:
this.low_ = low | 0; // force into 32 signed bits.
this.high_ = high | 0; // force into 32 signed bits.
我又去翻了一下各种资料,顺藤摸瓜,又找到了类似的文章:
JS 类型声明有固定写法,变量 | 0 表示整数,+变量表示浮点数。
var a = 1;
var x = a | 0; // x 是 32 位整数
var y = +a; // y 是 64 位浮点数
上面代码中,变量 x 声明为整数,y 声明为浮点数。支持 asm.js 的引擎一看到 x = a | 0,就知道 x 是整数,然后采用 asm.js 的机制处理。如果引擎不支持 asm.js 也没关系,这段代码照样可以运行,最后得到的还是同样的结果。
再看下面的例子。
// 写法一
var first = 5;
var second = first;
// 写法二
var first = 5;
var second = first | 0;
上面代码中,写法一是普通的 JavaScript,变量 second 只有在运行时才能知道类型,这样就很慢了,写法二是优化 js,second 在声明时就知道是整数,速度就提高了。
也许那么多年,chrome 内核早就对 js 运行期做了一定智能优化,再也不是傻傻的无类型语言了。感叹 JS 还真是在不断进化中。
1
codehz 2021-06-07 16:39:08 +08:00 1
确实有优化,v8 里叫做小整数,简称 SMI
(不过是有符号的,所以是 -2**32 到 2**32 - 1 的范围) 放数组里效果最明显,在没有 Uint8Array 等一系列数组的时候,就用纯数字数组也能实现很高的执行效率 可以看这里 https://v8.dev/blog/elements-kinds |
2
codehz 2021-06-07 16:40:06 +08:00
hmmm 打错了,是 2**31(
|
4
lichdkimba 2021-06-07 16:47:15 +08:00
不是很懂 阮一峰的 es6 教程写的是“JavaScript 所有数字都保存成 64 位浮点数”
https://es6.ruanyifeng.com/#docs/number#BigInt-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B 标准和浏览器的实现可能是两回事吧 |
5
3dwelcome OP @lichdkimba google 的官方代码应该没错。
也许从 chrome 某一版本开始,JS 内部语法就支持 var second = first | 0;这种特殊的整型数字定义,只是我们大部分人都不知道罢了。 |
6
geelaw 2021-06-07 16:56:58 +08:00
这是对注释的误解。参考 MDN:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number JavaScript 的 Number 等同于 IEEE 754 双精度浮点数。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR JavaScript 的按位或运算先把运算数转换成 32 位整数再运算,得到的是 Number 。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_plus JavaScript 的正号运算把运算数变成 Number 。 你写 var y = 1, z = +1, w = 1.0; 都是没有区别的。另外 JavaScript 的引擎的 “内部” 不需要 “用 IEEE 754 双精度浮点数” “表示” 一个 Number,只要执行效果和 IEEE 754 双精度浮点数一致即可。 楼主说的问题是特定于 asm.js 的人肉优化手段。另外 JavaScript 从来都不是“无类型”的。 |
7
3dwelcome OP @geelaw 我肯定信 google 不信你啊。
我绝对不相信 google 写出的 js 代码,会把 double 笔误写成 signed 32 bits of the long 。人家谷歌可是开发浏览器的,肯定不会犯这种低级错误。 而且 google 代码也不是编译成 asm.js ,和这个没关系。 |
8
muzuiget 2021-06-07 17:07:02 +08:00
> JS 类型声明有固定写法,变量 | 0 表示整数,+变量表示浮点数。
哪里看的垃圾文章,误人子弟。“| 0”表示对 a 和 0 进行位操作“或”,运算时,只是在 VM 里把两个操作数转成 32 整数计算,计算后回到 JS 里依然是个浮点数,只不过值在 32 位整数范围(就是 Google 那句注释的意思)。 > var first = 5; > var second = first | 0; 别笑死人了,某些 JS 转换器甚至做 tree shaking 会优化成 var second = 5 。 |
9
newmlp 2021-06-07 17:14:49 +08:00
js 里只有浮点数和 bigint 两种数字类型
|
10
geelaw 2021-06-07 17:18:06 +08:00
@3dwelcome #7 刚看明白,后面那段代码和 Google 没关系。但你对 Google 注释的误读仍然成立,若 a 是 Number,则 JavaScript 表达式 a | 0 对应 C# 表达式
double.IsNaN(a) ? 0.0 : (double)(int)(a) 其中假设 a 在 C# 里具有静态类型 double 。所谓 force into 32 signed bits 是指数值上的截断,不是类型上的改变。 |
11
3dwelcome OP |
12
3dwelcome OP @geelaw "所谓 force into 32 signed bits 是指数值上的截断,不是类型上的改变。"
尾巴上的|0, 就是个内部类型 hint 。代表了内部优先用 int32 位来表示。 JS 有运行期静态分析,如果函数里有复杂计算,Number 内部应该会自动退化到 double 。 |
13
cmdOptionKana 2021-06-07 17:50:17 +08:00 via Android
标准和实现分开说,就不用争论了。
|
14
lujjjh 2021-06-07 18:05:48 +08:00 1
你确实存在误解,#6 #10 说的是对的。如果我没有猜错,你在 #20 中的例子去掉 | 0 也是同样的效果,这个优化跟 | 0 完全没有关系,| 0 是应该说是某些场景下面给 compiler 的 hint 。
js 引擎的优化多了去了,规范还规定字符串都是 UTF-16,难道 js 引擎内部真的都用 UTF-16 存每个字符串么?只要在外部看起来符合规范,内部用什么黑科技优化都可以。 |
15
codehz 2021-06-07 18:34:21 +08:00 via Android 1
@3dwelcome
显然类型上是不会改变的,但是实现只要保证可观测的效果一致就可以了,内部用 smi 优化,对外表现还是和 double 一样,一点问题都没有 类似的优化在 v8 里还有字符串,被细分成单字节编码(仅 ASCII ),双字节编码,乃至还有给 JSON 留的特殊优化路径,这并不影响外部表现是 utf-16 字符串这件事。 只有在需要的时候,才会退化成性能表现更差的内部表示(然后同一个值再也不会回到优化的状态了) @lujjjh v8 的 JIT 会根据输入参数的内部表示类型做代码生成,一旦传入非 smi 数字,就有可能导致先前生成的代码失效从而影响性能,内部显式使用|0 之后,即使传入非 smi 数字,但是因为没有产生可观测的差异,所以不会破坏 JIT |
16
EPr2hh6LADQWqRVH 2021-06-07 19:00:41 +08:00
数字内部表示是 int64,但是在进行位运算时会被强转成 int32
|
17
EPr2hh6LADQWqRVH 2021-06-07 19:02:02 +08:00
这是 ECMA 里面位操作明确定义的
|
18
EPr2hh6LADQWqRVH 2021-06-07 19:03:58 +08:00
@avastms 是 double 不好意思,位运算强转 int32
|
19
secondwtq 2021-06-07 19:51:59 +08:00
是啊,再也不是傻傻的无类型语言了。我们 JS 真的是太厉害啦!
|
20
no1xsyzy 2021-06-08 09:42:29 +08:00
@3dwelcome 这个显然是优化,不然应当是一重间接指针的(因为是对象,scope -> number object -> stored data )
asm.js 的 `| 0` 的目的是入参强转,使得之后的代码可以很方便地 JIT |
21
no1xsyzy 2021-06-08 09:56:20 +08:00
随手试了下 IE,发现根本找不到(
谁能告诉我 Cheat Engine 怎么找 IE 的内存? |
22
no1xsyzy 2021-06-08 10:10:38 +08:00
|