JavaScript 浮点数取整

1,655 阅读9分钟

前言

  此文整理了JavaScript中常见的浮点数取整函数,当然也包括一些更为高效的位操作取整。

Math.trunc

  Math.trunc 用于返回数字的整数部分。

Math.trunc(-11.25) // -11
Math.trunc(NaN) // NaN
Math.trunc(Infinity) // Infinity
Math.trunc() // NaN

  Math.trunc内部也会调用Number将参数隐式转换为数值,然后再取整。

const object = {
  valueOf() {
    return 11.25
  }
}
Math.trunc(object) // 11

Math.round

  Math.round 用于四舍五入取整。

Math.round(-11.25) // -11
Math.round(NaN) // NaN
Math.round(Infinity) // Infinity
Math.round() // NaN
Math.round({ valueOf() { return 11.25 } }) // 11

Math.ceil

  Math.ceil 用于向上取整。

Math.ceil(-11.25) // -11
Math.ceil(NaN) // NaN
Math.ceil(Infinity) // Infinity
Math.ceil() // NaN
Math.ceil({ valueOf() { return 11.25 } }) // 12

Math.floor

  Math.floor 用于向下取整。

Math.floor(-11.25) // -12
Math.floor(NaN) // NaN
Math.floor(Infinity) // Infinity
Math.floor() // NaN
Math.floor({ valueOf() { return 11.25 } }) // 11

parseInt / Number.parseInt

  parseInt也能用于取整,只取出参数的整数部分。

ES6中将全局方法parseInt移植到了Number上,Number.parseIntparseInt 函数行为是一样的

Number.parseInt(11.25) // 11
Number.parseInt(Infinity) // NaN
Number.parseInt(NaN) // NaN
Number.parseInt() // NaN

  另外parseInt会将参数转换为字符串,将忽略字符串开头的空白符,从不是空格的字符开始处理,若第一个字符不是+-或者数字,将返回NaN,否则一直处理到不是数字字符为止。

Number.parseInt(' -11.25a') // -11
Number.parseInt('a11') // NaN
Number.parseInt({ toString() { return 11.25 } }) // 11

Number.prototype.toFixed

  Number.prototype.toFixed 可以用来格式化数值,当然也可以用来取整,并且根据小数位来决定四舍五入。

(-11.25).toFixed() // "-11"
(-11.55).toFixed() // "-12"

  但是注意toFixed可能会存在错误。

(1.005).toFixed(2) // "1.00"

位操作

  位运算中,参与运算的两个值会被转为有符号32位二进制数值(>>>无符号右移除外,会被转为无符号类型),超过32位的数值会被截断,而小数部分则会被舍弃

有符号32位的整型数值包括1个符号位和31个数值位,可表示的整数范围为[-(2 ^ 31 - 1), 2 ^ 31 - 1],即[-2147483647, 2147483647]。但是注意由于负数的表示方式为补码,1000 0000 0000 0000 0000 0000 0000 0000-0)用来表示-2147483648,因此有符号的32位整型数的表示范围为[-2147483648, 2147483647]

  例一,数值42949672972 ^ 32 + 2 ^ 0)与0或,其中左边第一位为符号位,结果中左边第一二位被截掉。

  01 0000 0000 0000 0000 0000 0000 0000 0001 // 4294967297
|    0000 0000 0000 0000 0000 0000 0000 0000 // 0
     0000 0000 0000 0000 0000 0000 0000 0001 // 1

  例二,数值21474836482 ^ 31)与0或,结果中左边第一位符号位截掉。

  0 1000 0000 0000 0000 0000 0000 0000 0000 // 2147483648
|   0000 0000 0000 0000 0000 0000 0000 0000 // 0
    1000 0000 0000 0000 0000 0000 0000 0000 // -2147483648

1000 0000 0000 0000 0000 0000 0000 0000在有符号的32位中表示-2147483648

  例三,数值-2147483649-(2 ^ 31 + 2 ^ 0))的二进制表示。

1 1000 0000 0000 0000 0000 0000 0000 0001 // 原码
1 0111 1111 1111 1111 1111 1111 1111 1110 // 反码
1 0111 1111 1111 1111 1111 1111 1111 1111 // 补码

  与0或,结果中左边第一位符号位被截掉。

  1 0111 1111 1111 1111 1111 1111 1111 1111 // -2147483649
|   0000 0000 0000 0000 0000 0000 0000 0000 // 0
    0111 1111 1111 1111 1111 1111 1111 1111 // 2147483647

x|0

  与0进行或运算。

-11.25|0 // -11

  小数部分被舍弃。

  1000 0000 0000 0000 0000 0000 0000 1011 // -11
| 0000 0000 0000 0000 0000 0000 0000 0000 // 0
  1000 0000 0000 0000 0000 0000 0000 1011 // -11

x&-1

  与-1进行与运算

-11.25&-1 // -11

  -132位二进制表示。

1000 0000 0000 0000 0000 0000 0000 0001 // 原码
1111 1111 1111 1111 1111 1111 1111 1110 // 反码
1111 1111 1111 1111 1111 1111 1111 1111 // 补码

  小数部分被舍弃。

  1000 0000 0000 0000 0000 0000 0000 1011 // -11
& 1111 1111 1111 1111 1111 1111 1111 1111 // -1
  1000 0000 0000 0000 0000 0000 0000 1011 // -11

x^0

  与0异或。

-11.25^0 // -11

  小数部分被舍弃。

  1000 0000 0000 0000 0000 0000 0000 1011 // -11
^ 0000 0000 0000 0000 0000 0000 0000 0000 // 0
  1000 0000 0000 0000 0000 0000 0000 1011 // -11

异或^可以想象为按位相加,但是不进位。比如0^0=0想象为0+0=00^1=1想象为0+1=1,而1^1想象为1+1=(1)0,不进位则为0

~~x

  双非运算。

~~-11.25 // -11

  小数部分被舍弃。

   1000 0000 0000 0000 0000 0000 0000 1011 // -11
~  0111 1111 1111 1111 1111 1111 1111 0100
~~ 1000 0000 0000 0000 0000 0000 0000 1011 // -11

x^n^n

  两次异或一个数。

-11.25^3^3 // -11

注意n^n=0,不管是0^0还是1^1,结果都是0。因此x^n^n等价于x^0

x<<0

  有符号左移0位,虽然没有进行移位,但是小数部分被舍弃。另外有符号移位意味着符号位保留不参与移位,只移动其余31位。

-11.25<<0 // -11

  11左移1位。

0000 0000 0000 0000 0000 0000 0000 1011 // 11
0000 0000 0000 0000 0000 0000 0001 0110 // 22

  -11左移1位。

1111 1111 1111 1111 1111 1111 1111 0101 // -11
1111 1111 1111 1111 1111 1111 1110 1010 // -22

正负数左移空位都是补0,另外<<左移n位相当于乘以2^n倍。用一个十进制数来做类比,20左移2位为2000,相当于20乘以10^2倍,也就是100

x>>0

  有符号右移0位。

-11.25>>0 // -11

  -1132位二进制表示。

1000 0000 0000 0000 0000 0000 0000 1011 // 原码
1111 1111 1111 1111 1111 1111 1111 0100 // 反码
1111 1111 1111 1111 1111 1111 1111 0101 // 补码

  -11右移1位。

1111 1111 1111 1111 1111 1111 1111 0101 // -11
1111 1111 1111 1111 1111 1111 1111 1010 // -6

  11右移1位。

0000 0000 0000 0000 0000 0000 0000 1011 // 11
0000 0000 0000 0000 0000 0000 0000 0101 // 5

注意负数右移空位补1,正数右移空位补0。另外>>右移n位相当于除以2^n倍,然后再向下取整(类似Math.floor)。

x>>>0

  无符号右移0位。

11.25>>>0 // 11

  但是无符号右移>>>只适用于正数的取整。

-1>>>0 // 4294967295

  出现以上现象是由于,Javascript会在移位之前做两种转换,第一是将非数值类型转换为0,第二则是将数值转换为无符号的32位二进制。

  第一种转换。

null>>>0 // 0

const fn = () => {}
fn>>>0 // 0

  第二种转换可以理解为,-1的有符号的32位二进制表示为。

1111 1111 1111 1111 1111 1111 1111 1111 // -1

  然后JavaScript会使其符号位失效,或者说符号位上的1不再用来表示负号,而是充当数值位,因此1111...1111转换为十进制为4294967295Math.pow(2,32) - 1)。

>>>0右移0位的作用。

  • 将任何非数值转换为0
  • 取出非负数的整数部分

  既然>>>使符号位失效了,那么可以使其再生效吗?

  当然,进行普通的位运算从而将其转换为有符号的32位数值。

-1>>>0|0 // -1
-1>>>0&-1 // -1
-1>>>0^0 // -1
~~(-1>>>0) // -1
(-1>>>0)>>0 // -1

🎉 写在最后

🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star ✨支持一下哦!

手动码字,如有错误,欢迎在评论区指正💬~

你的支持就是我更新的最大动力💪~

GitHub / GiteeGitHub Pages掘金CSDN 同步更新,欢迎关注😉~