es-number

Experience

#programming-experience

只要返回值为 number,就要考虑为 0 的情况下是否会有什么问题

基础

在 JavaScript 中, Number 是一种 定义为 64 位双精度浮点型(double-precision 64-bit floating point format) (IEEE 754) (numbers between -(2^53^ -1) and 2^53^ -1). 的数字数据类型。

它并没有为整数给出一种特定的类型。除了能够表示浮点数外,还有一些带符号的值:+Infinity-InfinityNaN (非数值,N 原地标记 ot-a-Number)。

超出这个范围,JavaScript 中的数字不再安全了,也就是只有 second mathematical interger 可以在 JavaScript 数字类型中正确表现。

浮点数

由于保存浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript 会不失时机地将浮点数值转换为整数值。

对于那些极大或极小的数值,可以用 e 表示法(即科学计数法)表示的浮点数值表示。

用 e 表示法表示的数值等于 e 前面的数值乘以 10 的指数次幂。ECMAScript 中 e 表示法的格式也是如此:

下面是一个使用 e 表示法表示数值的例子:

 var floatNum = 3.125e7;   // 等于 31250000 

也可以使用 e 表示法 表示极小的数值,如 0.00000000000000003,这个数值可以使用更简洁的 3e-17 表示。在默认情况下,ECMASctipt 会将那些小数点后面带有 6 个零以上的浮点数值转换为以 e 表示法表示的数值(例如,0.0000003 会被转换成 3e-7)。

浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1 加 0.2 的结果不是 0.3,而是 0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值。例如:

if (a + b == 0.3){          // 不要做这样的测试! 
    alert("You got 0.3."); 
} 

关于浮点数值计算会产生舍入误差的问题,有一点需要明确:这是使用基于 IEEE754 数值的浮点计算的通病,ECMAScript 并非独此一家;其他使用相同数值格式的语言也存在这个问题。

Number 构造函数

Number 对象主要用于创建一个数值,如果参数无法被转换为数字,则返回 NaN

console.log(Number('9/\n'))  // NaN
console.log(Number('9\n ')) // 9

在非构造器上下文中 (如:没有 new 操作符),Number 能被用来执行类型转换。

Number 的属性

Number.MIN_VALUE

Number.MIN_VALUE 属性表示在 JavaScript 中所能表示的最小的正值。

MIN_VALUE 属性是 JavaScript 里 最接近 0 的正值,而不是最小的负值。

MIN_VALUE 的值约为 5e-324。小于 MIN_VALUE ("underflow values") 的值将会转换为 0。

因为 MIN_VALUENumber 的一个静态属性,因此应该直接使用: Number.MIN_VALUE, 而不是作为一个创建的 Number 实例的属性。

MAX_VALUE

Number.MAX_VALUE 属性表示在 JavaScript 里所能表示的最大数值。

MAX_VALUE 属性值接近于 1.79E+308。大于 MAX_VALUE 的值代表 "Infinity"。

s 是符号位,表示正负。 m 是尾数,有 52 bits. e 是

MIN_SAFE_INTEGER

Number.MIN_SAFE_INTEGER 代表在 JavaScript 中最小的 安全 的 integer 型数字 (2531).

MIN_SAFE_INTEGER 的值是 -9007199254740991

MAX_SAFE_INTEGER

Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的 安全 整数(maxinum safe integer)(2531

MAX_SAFE_INTEGER 是一个值为 9007199254740991 的常量。

Number.MAX_SAFE_INTEGER // 9007199254740991
Math.pow(2, 53) - 1     // 9007199254740991
Number.MAX_VALUE == Number.MAX_SAFE_INTEGER // false

Number.MAX_SAFE_INTEGER 9007199254740991,小于该值能精确表示,不会有精度的丢失

Number.MAX_VALUE 1.7976931348623157e+308,大于该值得到的是 Infinity,介于 Infinity 和安全值之间的无法精确表示。

Number.NaN

Number.NaN 的值同全局对象 NaN 属性的值相同。

Number.isNaN(NaN) // true
Number.isNaN(Number.NaN) // true

NaN:举个例子,可以利用这个特殊行为来检测函数的参数是可运算的(可以像 number 一样进行加减乘除等运算)。如果不可运算,则可赋予这个参数一个默认的值或其他合适的内容。这样,就可以得到一个隐式转换参数值的函数,而这得益于 Javascript 的全功能性。

POSITIVE_INFINITY

Number.POSITIVE_INFINITY 属性表示正无穷大。

Number.POSITIVE_INFINITY 的值同全局对象 Infinity 属性的值相同。

与数学上的正无穷略有不同,具体查阅 MDN

NEGATIVE_INFINITY

Number.NEGATIVE_INFINITY 属性表示负无穷大。

Number.NEGATIVE_INFINITY 的值同全局对象 -Infinity 属性的值相同。

Number.EPSILON

Number 的方法

window.isNaN()

isNaN() 函数用来确定一个值是否为 NaN

描述

Number.isNaN()

ES6 新增

Number.isNaN() 方法确定传递的值是否为 NaN 和其类型是 Number。它是原始的全局 isNaN() 的更强大的版本。

描述

  isNaN(NaN) // true
  isNaN("NaN") // true
  Number.isNaN(NaN) // true
  Number.isNaN("NaN") // false
  Number.isNaN(1) // false

window.isFinite()

Number.isFinite()

ES6 新增

Number.isFinite() 方法用来检测传入的参数是否是一个有穷数(finite number)。

描述

Number.isInteger()

Number.isInteger() 方法用来判断给定的参数是否为整数。

描述

Number.isSafeInteger()

ES6 新增

Number.isSafeInteger() 方法用来判断传入的参数值是否是一个“安全整数”(safe integer)。

比如,2^53^- 1 是一个安全整数,它能被精确表示,在任何 IEEE-754 舍入模式(rounding mode)下,没有其他整数舍入结果为该整数。作为对比,2^53^ 就不是一个安全整数,它能够使用 IEEE-754 表示,但是 2^53^ + 1 不能使用 IEEE-754 直接表示,在就近舍入(round-to-nearest)和向零舍入中,会被舍入为 2^53^。

安全整数范围为 -(2^53^ - 1) 到 2^53^ - 1 之间的整数,包含 -(2^53^ - 1) 和 2^53^ - 1。

Number.parseInt()

ES6 新增

和全局函数 window.parseInt() 一致

Number.parseFloat()

ES6 新增

和全局函数 window.parseFloat() 一致

这种调整时为了使得全局变量的更加模块化

window.parseInt()

parseInt() 函数解析一个 字符串参数,并返回一个指定基数的整数 (数学系统的基础)。

参数

返回值

安全整数

可以解析的合法字符

示例

window.parseFloat()

parseFloat() 函数解析一个字符串参数并返回一个浮点数。

参数

返回值

描述

示例

Number 实例的方法 @@@

toFixed()

toFixed() 方法使用定点表示法来格式化一个数。

注意: toFixed 四舍五入采用银行家算法, 与传统的四舍五入有所不同, 不建议使用这个垃圾函数. toPercision 也有同样的问题

参数

返回值

抛出异常

描述

示例

  function financial(x) {
    return Number.parseFloat(x).toFixed(2);
  }
  
  console.log(financial(123.456));
  // expected output: "123.46"
  
  console.log(financial(0.004));
  // expected output: "0.00"
  
  console.log(financial('1.23e+6'));
  // expected output: "123000.00"
  
  console.log(financial('1.23e6'));
  // expected output: "123000.00"
  var numObj = 12345.6789;
  
  numObj.toFixed();         // 返回 "12346":进行四舍五入,不包括小数部分
  numObj.toFixed(1);        // 返回 "12345.7":进行四舍五入
  
  numObj.toFixed(6);        // 返回 "12345.678900":用0填充
  
  (1.23e+20).toFixed(2);    // 返回 "123000000000000000000.00"
  
  (1.23e-10).toFixed(2);    // 返回 "0.00"
  
  2.34.toFixed(1);          // 返回 "2.3"
  
  -2.34.toFixed(1);         // 返回 -2.3 (由于操作符优先级,负数不会返回字符串)
  
  (-2.34).toFixed(1);       // 返回 "-2.3" (若用括号提高优先级,则返回字符串)

toPrecision()

toPrecision() 方法以指定的精度返回该数值对象的字符串表示。

参数

返回值

异常

示例

  var numObj = 5.123456;
  console.log("numObj.toPrecision()  is " + numObj.toPrecision());  //输出 5.123456
  console.log("numObj.toPrecision(5) is " + numObj.toPrecision(5)); //输出 5.1235
  console.log("numObj.toPrecision(2) is " + numObj.toPrecision(2)); //输出 5.1
  console.log("numObj.toPrecision(1) is " + numObj.toPrecision(1)); //输出 5
  
  // 注意:在某些情况下会以指数表示法返回,为了不对数量级造成影响
  console.log((1234.5).toPrecision(2)); // "1.2e+3"

toExponential()

toExponential() 方法以指数表示法返回该数值 指数字符串 表示形式。

参数

返回值

注意

异常

示例

  var numObj = 77.1234;
  
  alert("numObj.toExponential() is " + numObj.toExponential()); //输出 7.71234e+1
  alert("numObj.toExponential(4) is " + numObj.toExponential(4)); //输出 7.7123e+1
  alert("numObj.toExponential(2) is " + numObj.toExponential(2)); //输出 7.71e+1
  alert("77.1234.toExponential() is " + 77.1234.toExponential()); //输出 7.71234e+1
  alert("77 .toExponential() is " + 77 .toExponential()); //输出 7.7e+1

三者的差别

toFixed(),有效的小数

toPrecision(),指定多少位有效数字,相当于是数学题的经典坑人伎俩

toExponential(),转换为指数形式,顺便可以指定小数位数

toString()

toString() 方法返回指定 Number 对象的字符串表示形式。

参数

描述

toLocaleString()

NaN 的产生条件

0 除以 0 会返回 NaN —— 但是其他数除以 0 则不会返回 NaN

0 乘以 POSITIVE_INFINITY 为 NaN。

NaN 乘以 POSITIVE_INFINITY 为 NaN。

含有 NaN 的算数表达式

Math 对象

Math 属于一个工具类,它不需要我们创建对象,它里边封装了属性运算相关的常量和方法

虽然是大写的,但是不是一个构造函数 @@@

我们可以直接使用它来进行数学运算相关的操作

属性

Math.LN10

10 的自然对数, 约等于 2.303.

Math.PI

常量,圆周率

其他属性都是常量

方法

隐式转换

调用 Number()

如果有任一参数不能被转换为数值,则结果为 NaN,Math 全部方法均是如此

Math.abs()

绝对值运算

Math.ceil()

天花板

向上取整

无论正负非整数一定变大

Math.floor()

向下取整

无论正负非整数一定变小

Math.floor(-2.5) // -3

Math.round()

四舍五入取整

Math.random()

生成一个 0-1 之间的随机数

生成一个 x-y 之间的随机数

Math.round(Math.random()*(y-x)+x);

Math.pow(x,y)

求 x 的 y 次幂

Math.log()

返回一个数的自然对数(log~e~, 即 ln)。

Math.sqrt()

对一个数进行开方

Math.max()

求多个数中最大值

参数

返回值

Math.min()

求多个数中的最小值

Math.sin/cos/tan()

需要注意的是三角函数(sin(), cos(), tan(),asin(), acos(), atan(), atan2() 是以弧度返回值的。可以通过除法(Math.PI / 180)把弧度转换为角度,也可以通过其他方法来转换。

ES6 新增

Math.cbrt()

Math.cbrt 方法用于计算一个数的立方根。

Math.sign()

Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

Math.trunc()

Math.trunc 方法用于去除一个数的小数部分,返回整数部分。

Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0

对于非数值,Math.trunc 内部使用 Number 方法将其先转为数值。

Math.trunc('123.456') // 123
Math.trunc(true) //1
Math.trunc(false) // 0
Math.trunc(null) // 0

对于空值和无法截取整数的值,返回 NaN

Math.trunc(NaN);      // NaN
Math.trunc('foo');    // NaN
Math.trunc();         // NaN
Math.trunc(undefined) // NaN

对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.trunc = Math.trunc || function(x) {
  return x < 0 ? Math.ceil(x) : Math.floor(x);
};

与已有 API 的区别

简单的说,parseInt() 主要用于将字符串转换成整数,所以哪怕目标本身就是一个数,也极有可能是先转换成字符串再来处理的,这也能解释科学计数法的结果

Math.trunc 是直接对数值进行处理,理论上来说会快一些也更准确一些。可惜有些浏览器不支持。所以现在用 Math.floorMath.ceil 的比较多,但是要注意处理负数。

Math.floor(-5.1) // -6
Math.ceil(-5.1) // -5

进制

Number('0b111') // 7

Number('0o10') // 8

Number('0xF') //15

进制表示法

十六进制前缀 0x

ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b(或 0B)和 0o(或 0O)表示。

0b111110111 === 503 // true
0o767 === 503 // true

从 ES5 开始,在严格模式之中,八进制就不再允许使用前缀 0 表示,ES6 进一步明确,要使用前缀 0o 表示。

// 非严格模式
(function(){
  console.log(0o11 === 011);
})() // true

// 严格模式
(function(){
  'use strict';
  console.log(0o11 === 011);
})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.

合法数值

ES 的合法数值进制是十进制、二进制、八进制、十六进制,除了十进制都需要添加前缀

在控制台打印、进行算数运算的时候的时候会自动转换成十进制

console.log(0x11) // 17
console.log(0o11) // 9
console.log(0b11) // 3
console.log(0b11+5) // 3

十进制数值,还可以使用 e 表示法

console.log(.1e1) // 1

注意

注意

  0x12e3 === 4835
  0x12e+3 === 305
  0x12e-3 === 299
  0x12 === 18

进制转换(进制形式字符串 To 数值)

只要是 合法数值的字符串形式,都可以通过函数转换为相应的数值

有 2 个函数可以把非数值转换为数值间的 进制转换:Number()、parseInt()

parseFloat() 方法无法识别二进制、八进制、十六进制合法表示的字符串

二进制和八进制

如果要将 0b0o 前缀的字符串数值转为十进制,要使用 Number 方法

Number('0b111')  // 7
Number('0o10')  // 8

parseInt('0o11') // 0
parseInt('0b11') // 0
parseInt('0o11',8) // 0
parseInt('0b11',2) // 0
Number('0o11') // 9
Number('0b11') // 3

十六进制

如果要将 0x 前缀的字符串数值转为十进制,可以使用 Number()、parseInt()

parseInt('0xF') // 15
parseFloat('0xF') // 0
Number('0xF') //15

进制转换 (数值 to 字符串)

Number.prototype.toString()

toString() 方法返回指定 Number 对象的字符串表示形式。

参数

描述

  var count = 10;
  
  console.log(count.toString());    // 输出 '10'
  console.log((17).toString());     // 输出 '17'
  console.log((17.2).toString());   // 输出 '17.2'
  
  var x = 6;
  
  console.log(x.toString(2));       // 输出 '110'
  console.log((254).toString(16));  // 输出 'fe'
  
  console.log((-10).toString(2));   // 输出 '-1010'
  console.log((-0xff).toString(2)); // 输出 '-11111111'

进制转换(进制形式字符串 To 进制形式字符串)

ES 无法直接进行数值到数值的进制转换,原因如下:

要完成从数值到 ' 数值 ' 的转换,只能先变成十进制,再通过 toString 方法

下面是一个十六进制数转二进制数的例子

var hexadecimal = '0xF'
// 先变成十进制字符串
let binary = parseInt(hexadecimal).toString(2)
console.log(binary) // '1111'

进制转换算法

利用栈的逆序性质

第一次模除的结果是进制的最后一位,余下的

倒数第二位,就是除以 2 之后再次模除,乘以二就是左移一位,除以二就是右移一位,之后再次判断余下的是什么

每次都除以 radix,就是模拟 a~n~ * radix^n-1^

最后再倒过来就是结果

从十进制到二进制

  function divideBy2(decNumber) {
  
    const remainderStack = new Stack()
    let remainder,
        binaryString = ''
  
    while (decNumber > 0) { 
      // 取余数, 向下取整忽略非整数的部分
      remainder = Math.floor(decNumber % 2)
      // 保存余数
      remainderStack.push(remainder)
      // 除以2向下取整
      decNumber = decNumber>>>1
    }
  
    while (!remainderStack.isEmpty()) { 
      binaryString += remainderStack.pop().toString();
    }
  
    return binaryString;
  }
  
  console.log(divideBy2(12.2)) // .2 会在第一轮循环之后被忽略

通用转换算法

 function divideByRadix(decNumber,radix) {
 
   var remainderStack = new Stack(),
     remainder,
     radixString = '',
     digits = {
       [2]:'01',
       [4]:'0123',
       [8]:'01234567',
       [16]:'0123456789ABCDEF',
     }
 
   while (decNumber > 0) { //{1} 
     remainder = Math.floor(decNumber % radix); //{2} 取余数,加上向下取整忽略非整数的部分
     remainderStack.push(remainder); //{3} 保存余数
     decNumber = Math.floor(decNumber / radix); //{4} 除以base向下取整
   }
 
   while (!remainderStack.isEmpty()) { //{5} 
     radixString += digits[radix][remainderStack.pop()]; // 根据余数取对应的字符
   }
 
   return radixString;
 }
 
 console.log(divideByRadix(15,16)) // F

FAQ

#faq/js

Number 类型转换

es-basic

浮点数计算

function numberEqual(a,b){

  return Math.abs(a-b)<Number.EPSILON
}
let a = 0.1+0.2, b=0.3
console.log(numberEqual(a,b));

function numbersequal(a,b){ return Math.abs(a-b)<Number.EPSILON;} 
var c=2.1+2.2, d=2.3;
console.log(numbersequal(c,d)); //true

判断一个数是否是 Number

typeof number

判断一个数是否是整数

ES6 提供了 Number.isInteger

Number.isInteger(3) // true
Number.isInteger(3.1) // false
Number.isInteger('') // false
Number.isInteger('3') // false
Number.isInteger(true) // false
Number.isInteger([]) // false

方法一:模除判断

对于空字符串、字符串类型数字、布尔 true、空数组都会发生隐式转换,需要先判断类型

function isInteger(obj) {
 return typeof obj === 'number' && obj%1 === 0
}

方法二:使用 Math.round、Math.ceil、Math.floor 判断

整数取整后还是等于自己。利用这个特性来判断是否是整数,Math.floor 示例,如下

function isInteger(obj) {
 // 如果发生了隐式转换也不OK
 return Math.floor(obj) === obj
}

isInteger(3) // true
isInteger(3.3) // false
isInteger('') // false
isInteger('3') // false
isInteger(true) // false
isInteger([]) // false

方式三、通过 parseInt 判断

function isInteger(obj) {
 return parseInt(obj, 10) === obj
}

isInteger(3) // true
isInteger(3.3) // false
isInteger('') // false
isInteger('3') // false
isInteger(true) // false
isInteger([]) // false、

//很不错,但也有一个缺点 #,太大了被截取了
isInteger(1000000000000000000000) // false

通过位运算判断

function isInteger(obj) {
 return (obj | 0) === obj
}

isInteger(3) // true
isInteger(3.3) // false
isInteger('') // false
isInteger('3') // false
isInteger(true) // false
isInteger([]) // false

//这个函数很不错,效率还很高。但有个缺陷,上文提到过,位运算只能处理32位以内的数字,对于超过32位的无能为力
isInteger(Math.pow(2, 32)) // 32位以上的数字返回false了

保留小数

toFixed() 方法会存在后导零,而且是字符串

通过 Number(),可以消除后导零,转换为浮点数

console.log(
  (1234.5).toFixed(2), // '1234.50'
  (1234.5).toFixed(2)*1, // 1234.5
)

保留有效数字:toPrecision()

例如:对于 X 进行保留两位小数的处理,则可以使用 Math.round(X * 100) / 100.进行处理。

js toFixed 四舍五入问题 - 掘金

拆分整数和小数

处理成整数,然后相减得到小数

转换成字符串,使用 split

转换成千分位数字

字符串匹配

es-regexp

方法二

function format1(number) {
  return Intl.NumberFormat().format(number)
}

方法三

function format2(number) {
  return number.toLocaleString('en')
}

转换成百分数

先乘 100,在保留小数,再拼串