js--数字精度与范围、位运算、逻辑运算

IEEE754的存储格式--64bit

  • 求值方法:(-1)S*(1.M)*2(E-1023)
  • 64位,1位总的符号位S,11位阶码E(移码),53位尾数位M(原码),其中1位隐藏。
  • 阶码用移码表示,主要控制小数点在尾数部分的跳转,11位阶码中,又有1位表示符号位,10位来表是数字,按无符号数来计算,原本可以表示02047的数据,但除去阶码为全0、全1的情况,剩下可以表示12046种情况,移码偏置是+1023,所以所有从数位上换算出来的数字,都要 减去 1023,得到的才是阶码的真实值。真实值的范围是:-1022~1023。所以指数范围为2^-1022 ~ 2^1023。
    -尾数部分默认规格化后是1.xxx,1省略不写,主要控制小数部分的精确数字,是表示数字的主要部分。位数部分分为规格化和非规格化,范围比较复杂,下面再进行讨论。
  • 表示0时,所有位数全为0
  • 表示无穷大时,阶码全为1,尾数全为0,符号位指示是正无穷还是负无穷。

数值范围

浮点数

  • 最小正浮点数: Number.MIN_VALUE = 5e-324。

  • 解析: 这个值等价于(Math.pow(2,-52) ) * Math.pow(2,-1022) = Math.pow(2,-1074)。

    • 首先取阶码为最小值Math.pow(2,-1022),这里不用多说。
    • 然后是取尾数为最小值,按照IEEE754的标准,尾数部分规格化以后隐含还有1位,最小值是1.0000....0001(小数后第52位为1,其他为0),此时的最小值是1+Math.pow(2, -52)。但是其实IEEE754还有另外一条规则,规定当浮点数的指数为允许的最小指数值,尾数不必是规范化的。当作为非规格化数据时,尾数部分所能表达的最小值是Math.pow(2, -52)。因此这里所取的最小正尾数就是非规格化的数据。
    • 所以最后得到的最小正浮点数就是Math.pow(2,-1074);
    • 补充:为了保存非规范浮点数,IEEE 标准采用了类似处理特殊值零时所采用的办法,即用特殊的指数域值 emin - 1 加以标记,而一般来说,阶码移码的偏置值都是emin+1,此时进行了emin-1+(emin+1)以后,阶码的值全为0,此时如果尾数为0且符号位也为0,则表示的值就是0;如果尾数不为0,表示的就是非规格化的数据。
      • 例: 想要表示0.0001 × 2^-125,表达为规范浮点数则为 1.0 × 2-129。问题在于其指数大于允许的最小指数值,所以无法保存为规范浮点数。此时可以保存为非规格化数据,先将阶码化为能表示的最小值,0.001 * 2 ^ -126,阶码经过-1处理为:-126 -1 +127 = 0。 尾数部分小数点左边的0要像规格化中的1一样省略,所以这个数据最终表示为(32bit形式):0(符号位) 0000 0000(阶码) 0010 000000.....(剩余尾数部分全为0) = 0010 0000 (十六进制)
  • 最大正浮点数: Number.MAX_VALUE = 1.7976931348623157e+308 。

  • 解析:等价于 (Math.pow(2,53) - 1) * Math.pow(2,971)

  • 首先看尾数部分,表示为最大值时是1.1111...111(小数点右边52个1),在不考虑小数点带来的影响时,53个1的数据表示为(Math.pow(2,53) - 1)

  • 然后看阶码部分,阶码所能表示的最大值是1023,但是为了让前面的尾数部分的小数点跳到有效值的最后,要让1023-52 = 971.所以阶码部分最大值为Math.pow(2,971)

  • 所以最终的最大值为:(Math.pow(2,53) - 1) * Math.pow(2,971)

连续整数

  • 最大正整数(连续且精确):Math.pow(2, 53) =9007199254740992

  • 解析:这里之所以没有减一,是因为这是浮点数,可以用阶码来控制小数点的跳转。

  • 如果是Math.pow(2, 53) - 1,其表示为:

    • 尾数部分为53个1,所表示的值是1.11111....1( 小数点后52个1 )
    • 阶码部分要表示的值是52,加上偏置1023,结果是1075,表示为二进制是:100 0011 0011。
    • 所以最终这个值表示为 0 | 100 0011 0011 | 111111...111(52个1)
  • 如果是Math.pow(2, 53),其表示为:

    • 尾数部分为1.000...000(小数点后52个0)
    • 阶码部分要移53位,加上偏置值1023,结果是1076,表示为二进制是:100 0011 0100.
    • 所以最终这个值表示为 0 | 100 0011 0100 | 0000...000(52个0,小数点左边的1被默认省略)
  • 因此,Math.pow(2, 53) 也是可以准确表示出来的,但如果大于Math.pow(2, 53) ,由于尾数部分不够位数来精确地指定某个值,就会发生四舍五入的情况,带来精度误差。

  • 最小负整数(连续且精确):Math.pow(2,-53) = -9007199254740992

  • 解析:表示方法就是Math.pow(2,-53)的符号位为1。

    • 1 100 0011 0100 0000...000(52个0,小数点左边的1被默认省略)
但是还要注意,虽然我们确定Math.pow(2,-53) 和Math.pow(2,53) 都可以准确表示出来,但是在JS中的安全数还是要在这个基础上 减1
 Number.MAX_SAFE_INTEGER = 9007199254740991
 Number.MIN_SAFE_INTEGER = -9007199254740991

补充:

  • 对于位运算,js仅支持32位整数,其中1位符号位,31位数字位。

  • 32bit最大正整数:Math.pow(2, 31) - 1 =2147483647

  • 32bit最小负整数:Math.pow(2, 31) = -2147483648

  • 【个人觉得当做32位处理时,用的应该是补码】

  • 位运算:

  • ~ 按位非(NOT)

    • 包括符号位在内,按位取反。本质是:在原操作数上添加负号,再减1
  • & 按位与(AND)

    • 包括符号位,用与逻辑运算。
  • | 按位或(OR)

    • 包括符号位,用或逻辑运算。
  • ^ 按位异或(XOR)

    • 包括符号位,相异为1,相同为0.
  • << 左移

    • 左移不改变符号位,右边空缺补0.
    • 本质是原数乘以2的移位数次方。
  • . >> 右移

    • 不改变符号位,即用符号位来填补左边出现的空缺。
    • 本质是原数除以2的移位数次方。
  • . >>> 无符号右移

    • 不论正负,左边出现的空缺都用0来填补。
  • || 或运算

    • 逻辑运算过后如果为true,取第一个为true的值,如果为false,取最后一个为false的值
    • 短路规则:var a = “” || null || 3 || 4 —-> var a = fasel || false || true || true 结果为true 则返回第一个true,即是3
  • && 与运算

    • 逻辑运算过后如果为true,取最后一个为true的值,如果为false,取第一个为false的值
    • 短路规则:var b = 4 && 5 && null && 0 ——> var b = true && true && false && false 结果是false 则返回第一个false 即是null .
  • 如果某次计算结果得到了一个超出javascript数值范围的值,这个值会自动转化成Infinity。(Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY)

  • 想要确定一个值是不是有穷的,可以使用 isFinite( num ) 函数。

  • 想要确定一个值是不是NaN,可以使用isNaN( num )函数,但这里并不是只有NaN才会使得该函数返回true,任何对象以及非纯数字类型的字符串,都会使该函数返回true,真正判断一个数据是否是NaN的,是用 a==a来判断。如果是NaN,则返回false.

你可能感兴趣的:(js--数字精度与范围、位运算、逻辑运算)