# JavaScript 浮点数取整

# 前言

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

# Math.trunc

  ;Math.trunc (opens new window) 用于返回数字的整数部分。

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 (opens new window) 用于四舍五入取整。

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 (opens new window) 用于向上取整。

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 (opens new window) 用于向下取整。

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.parseInt (opens new window)parseInt (opens new window) 函数行为是一样的

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 (opens new window) 可以用来格式化数值,当然也可以用来取整,并且根据小数位来决定四舍五入。

(-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 (opens new window) ✨支持一下哦!

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

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

GitHub (opens new window) / Gitee (opens new window)GitHub Pages (opens new window)掘金 (opens new window)CSDN (opens new window) 同步更新,欢迎关注😉~

最后更新时间: 3/6/2022, 9:06:37 PM