parseInt(8).toString(2)
0.625 * 2 = 1.25
0.25 * 2 = 0.5
0.5 * 2 = 1
console.log(parseFloat(8.625).toString(2))
莫得问题~
但是计算机存储数据肯定不会存储小数点的呀,毕竟计算机底层只有0和1
那计算机怎么储存浮点数咧?
虽然JavaScript不是强类型语言,所以对于Number类型不会严格区分整型数,浮点数等,但是在JavaScript内部,数值都是以64位浮点数的形式储存
根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:
(1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。 (2)M表示有效数字,大于等于1,小于2。 (3)2^E表示指数位。
举例来说:
十进制的5.0,写成二进制是101.0,相当于1.01×2^2。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十进制的 -5.0,写成二进制是 -101.0,相当于 -1.01×2^2。那么,按照上面V的格式,可以得出s=1,M=1.01,E=2。
IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
IEEE 754对有效数字M和指数E,还有一些特别规定。
前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。**IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。**比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以64位浮点数为例,留给M只有52位,将第一位的1舍去以后,等于可以保存53位有效数字
(这就解答了为什么JS的最大安全整数为什么是53位,因为JS的数值是用64位数字进行存储,但是有1位是符号位,11位是指数位,只剩53位存储数字)
至于指数E,情况就比较复杂。
首先,E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0255;如果E为11位,它的取值范围为02047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,
E的真实值必须再减去一个中间数
,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
比如,2^10的E是10,所以保存成64位浮点数时,必须保存成10+1023=1033,即10000001001
然后,指数E还可以再分成三种情况:
E不全为0或不全为1。这时,浮点数就采用上面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
E全为0。这时,浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数(NaN)。
既然公式都有了,那计算机存储浮点数只需要存储这三个变量即可
浮点数计算机存储 = 存储s + E + M的二进制
const a = 2.25
//a转二进制
parseFloat(a).toString(2)
//10.01-> (-1)^0*1.001*2^1
//套公式
s = 0
E = 1024
M = 1.001
//答案:0+10000000000+001+`(50个0)`
这下我们知道JavaScript是用64位存储数字,内部除去1位符号位,11位指数为,应该还有52位用来存储有效数字,话是这么说没错,但事实上,位操作符并不是这么认为的
。在 ECMAScript® Language Specification 中是这样描述位操作符的:
The production A : A @ B, where @ is one of the bitwise operators in the productions above, is evaluated as follows:
Let lref be the result of evaluating A.
Let lval be GetValue(lref).
Let rref be the result of evaluating B.
Let rval be GetValue(rref).
Let lnum be ToInt32(lval).
Let rnum be ToInt32(rval).
Return the result of applying the bitwise operator @ to lnum and rnum. The result is a signed 32 bit integer.
需要注意的是第5和第6步,按照ES标准,两个需要运算的值会被先转为有符号的32位整型(划重点!!!!!先被转成有符号的int类型)。所以超过32位的整数会被截断,而小数部分则会被直接舍弃。
ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。在 ECMAScript 中,所有整数字面量默认都是有符号整数,这意味着什么呢?
有符号整数使用 31 位表示整数的数值,用第 32 位表示整数的符号,0 表示正数,1 表示负数。数值范围从 -2147483648 到 2147483647。
这些也就能解释上面的诡异情况啦~