目录

🔢 JavaScript 数字类型

在 JavaScript 中,数字类型中两种:

  • 常规数字以 64 位的格式 IEEE-754 (opens new window) 存储,也被称为「双精度浮点数」,安全范围在:-253 ~ 253 。常用的数字以这种为主。
  • BigInt 数字,用于表示任意长度的整数。仅在少数特殊领域才会用到 BigInt 类型的数字。

# 数字的表示方法

# _ 的使用

下划线可用作分隔符(是语法糖的角色),使得数字具有更强的可读性。JavaScript 引擎会直接忽略数字之间的 _

🌰 例子 / 下划线分割数字:

let billion = 1000000000; // before
let billion = 1_000_000_000; // after
1
2

# 科学计数法

在 JavaScript (许多编程语言中),使用 e 表示为 10 的几次方,由 e 后面跟着的定量数字决定,可以是正数也可以是负数。

🌰 例子:

let billion = 1e9;  // 10 亿,字面意思:数字 1 后面跟 9 个 0
1

# 二进制、八进制、十六进制

二进制和八进制数字系统很少使用,但也支持使用 0b0o 前缀:

let a = 0b11111111; // 二进制形式的 255
let b = 0o377; // 八进制形式的 255

alert( a == b );
1
2
3
4

十六进制数字在 JavaScript 中被广泛用于表示颜色,编码字符以及其他许多东西。 0x ,然后是数字。

alert( 0xff ); // 255
alert( 0xFF );
1
2

对于其他进制,应该使用函数 parseInt

# 与数字类型有关的方法

# toString(base)

使用此方法 num.toString(base) 返回在给定 base 进制数字系统中 num 的字符串表示形式(将二进制数字转换为相应的 base 进制,对应反过来的方法是 parseInt(str, radix))。

base 的范围可以从 236 。默认情况下是 10

  • base=16 用于十六进制颜色,字符编码等,数字可以是 0..9A..F
  • base=2 主要用于调试按位操作,数字可以是 01
  • base=36 是最大进制,数字可以是 0..9A..Z 。所有拉丁字母都被用于了表示数字。

🌰 例子 :

let num = 255

console.log(num.toString(16)) // 转为十六进制
console.log(num.toString(2)) // 转为二进制
1
2
3
4

🌰 例子 / 将较长的数字使用 36 数字系统表示:

console.log(12345678..toString(36)) // 7clzi
1

.. 两个点调用方法:想直接在一个数字上调用一个方法,比如上面例子中的 toString ,那么需要在它后面放置两个点 .. 。这是因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,现在使用该方法。

也可以使用括号分隔 (123456).toString(36)

# 数字的舍入

关于数字进行舍入的内建函数

  • Math.floor 向下舍入。例如, 3.1 变成 3-1.1 变成 -2
  • Math.ceil 向上舍入。例如, 3.1 变成 4-1.1 变成 -1
  • Math.round 向最近的整数舍入3.1 变成 33.6 变成 4 ,中间值 3.5 变成 4
  • Math.trunc 移除小数点的所有内容(没有舍入)。例如, 3.1 变成 3-1.1 变成 -1

常用的处理数字小数部分(移除)就是上述这些方法。

如果想要保留数字的小数部分(舍入到后 n 位),有两种方法:

  • toFixed(n) :将数字舍入到小数点后 n 位(规则四舍五入,向上或向下舍入到最接近的值),例如:

    let num = 12.34;
    num.toFixed(1); // 12.3
    
    1
    2
    let num = 12.36;
    num.toFixed(1); // 12.4
    
    1
    2

    toFixed 的结果是一个字符串。如果小数部分比所需要的短,则在结尾添加零(补上不足的小数位):

    let num = 12.34;
    alert( num.toFixed(5) ); // "12.34000"
    
    1
    2
  • 乘除法:例如,要将数字舍入到小数点后两位,我们可以将数字乘以 100 ,调用舍入函数,然后再将其除回。例如:

    let num = 1.23456;
    Math.round(num * 100) / 100 // 1.23456 -> 123.456 -> 123 -> 1.23
    
    1
    2

# JavaScript 小数数字精度

JavaScript 中数字精度问题:

点击查看

在内部,数字是以 64 位格式 IEEE-754 (opens new window) 表示的,所以有 64 位可以存储一个数字:其中 52 位存储这些数字,11 位用于存储小数点的位置(对于整数,它们为零),而 1 位用于符号。

如果一个数字真的很大,则可能会溢出 64 位存储,变成一个特殊的数值 Infinity

console.log(1e500) // Infinity
1

🌰 例子 / 一个常见的小数加法运算,并不是预期的结果:

alert( 0.1 + 0.2 ); // 0.30000000000000004
1

在 JavaScript 中,一个数字以其二进制的形式存储在内存中,一个 1 和 0 的序列。但是在十进制数字系统中看起来很简单的 0.10.2 这样的小数,实际上在二进制形式中是无限循环小数。

IEEE-754 数字格式通过将数字舍入到最接近的可能数字来解决此问题。这些舍入规则通常不允许看到「极小的精度损失」,但是它确实存在。

alert( 0.1.toFixed(20) ); // 0.10000000000000000555
1

所以当对两个数字进行求和时,它们的「精度损失」会叠加起来。这就是为什么 0.1 + 0.2 不等于 0.3

解决方法:

  • 使用 toFixed(n) 对结果舍入。

🌰 例子:

let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // toFixed 总是返回一个字符串
alert( +sum.toFixed(2) ); // 使用一元加号将其强制转换为一个数字
1
2
3
  • 将小数临时乘以小数位数的 10 的倍数,先将其转换为整数,进行数学运算,然后再除回相应的 10 的倍数。但是这种方法只可以减少误差,但不能完全消除误差。

🌰 例子:

alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
1
2

小数的运算处理是不可能完全避免的。就算所有的价格数据都是整数,也避免不了打折计算结果的情况。所以最好的方法就是在得到计算小数的结果进行「剪掉尾巴」的处理,

提示

JavaScript 数字内部存在两个零, -00 ,这是因为在存储时,使用一位来存储符号,因此对于包括零在内的任何数字,可以设置这一位或者不设置。在大多数情况下,这种区别并不明显,运算符将它们视为相同的值

# isFiniteisNaN

对于两个特殊的数值: Infinity-Infinity )(比任何数值都大的特殊数值)和 NaN (计算出错的结果),它们属于 number 类型,但不是「普通」的数字,因此,这里有用于检查它们的特殊函数:

  • isNaN(value) 将其参数转换为数字,然后测试它是否为 NaN

    console.log( isNaN(NaN) ); // true
    console.log( isNaN("str") ); // true
    
    1
    2

    注意:不能使用与 NaN 全等比较, NaN 独一无二的,它不等于任何东西,包括它自身。

    console.log(NaN === NaN)
    
    1
  • ⭐️ isFinite(value) 将其参数转换为数字,如果是常规数字,则返回 true ,而不是 NaN/Infinity/-Infinity ,(可以理解为检验一个数字是否为有限值(不是 NaN 或者 Infinity )):

    console.log(isFinite("15")) // true
    console.log(isFinite("str")) // true
    console.log(isFinite(Infinity)) // false 
    
    1
    2
    3

    还被用于验证字符串值是否为常规数字

    let num = +prompt("Enter a number", '');
    console.log(isFinite(num));// 结果会是 true,除非你输入的是 Infinity、-Infinity 或不是数字
    
    1
    2

注意

在所有的数字方法函数中,包括 isInfinity ,空字符串或者仅有空格的字符串均被视为 0

提示

** 更可靠的比较方法 Object.is **:这是一个特殊的 JavaScript 内建方法,它类似于 === 一样对值进行比较,但它对于两种边缘情况更可靠:

  • NaNObject.is(NaN,NaN) === true
  • 0-0 是不同的: Object.is(0,-0) === false 。(从技术上看,数字的字符位可能会不同,这就导致数字并不相同,即使其他所有位都为零)

其他情况, Object.is(a,b) 完成的比较结果与 a === b 结果相同。

这种比较方式经常被用在 JavaScript 规范中。当内部算法需要比较两个值是否完全相同时,它使用 Object.is (内部称为 SameValue (opens new window))。

提示

JavaScript 中检验用户的输入问题:现有一函数需要将用户输入的数值相加,返回相加后的结果。所以要判断用户的输入(完整考虑,不能仅仅只将用户的输入转换为数字):

function add(){
  let a = +prompt('first num', 0)
  if(!isFinite(a)) return alert('error')
  
  
  let b = +prompt('second num', 0)
  if(!isFinite(b)) return alert('error')
  
  return alert(a + b)
}
1
2
3
4
5
6
7
8
9
10

# parseIntparseFloat

前面所述,当我们需要将计算结果或者某一个值转换为数字类型,可以选择使用 + 或者 Number() 转换。使用这两种方法对于如果一个值不完全是数字(带有单位),就会失败:

console.log(+"100px") // NaN
1

但是对于字符串中存在空格(无论开头结尾),都会被忽略成功转换。所以这两种方法存在局限。

对于带有单位( '100px'‘12pt’“19€” )的数字字符串,要提取其中的数值,JavaScript 提供了 parseIntparseFloat ,可以从字符串中读取出数字,直到无法读取:

  • parseInt 返回一个整数;
  • parseFloat 返回一个浮点数;

🌰 例子:

console.log(parseInt('100px')) // 100
console.log(parseFloat('12.5em')) // 12.5

console.log(parseInt('12.5')) // 12 提取整数
console.log(parseFloat(3.14.159)) // 3.14 停止在第二个小数点

console.log(parseInt('a123')) // NaN 第一个符号即非数值,则停止读取,对于parseFloat也同理
1
2
3
4
5
6
7

提示

parseInt(str, radix) 的第二个参数: radix 指定了数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串(转换为十进制整数):

console.log(parseInt('0xff', 16)) // 转换16进制的数为十进制
console.log(parseInt('ff', 16)) // 不带0x仍然有效

console.log(parseInt('2n9c', 36)) // 123456
1
2
3
4

# 其他数学函数

  • Math.random()返回一个从 0 到 1 的随机数(不包括 1。)

    console.log(Math.random()) // 0.5181640579695068
    
    1

    与舍入的方法配合使用:

    Math.floor(Math.random()*10) // 0~10之间的随机整数
    Math.ceil(Math.random()*10) // 0~10之间的随机整数
    Math.round(Math.random()*10) // *均衡获取0~10之间的随机整数
    
    1
    2
    3

更详细的生成随机数的方案:

点击查看
  • 生成从 minmax 的随机整数:

    错误方案 / 边缘值的概率会比其他值少 :

    function randomInteger(min, max) {
      let rand = min + Math.random() * (max - min);
      return Math.round(rand);
    }
    
    1
    2
    3
    4

    正确方案 / 调整取值范围的边界:

    function randomInteger(min, max) {
      // 现在范围是从  (min-0.5) 到 (max+0.5)
      let rand = min - 0.5 + Math.random() * (max - min + 1);
      return Math.round(rand);
    }
    
    1
    2
    3
    4
    5

    正确方案 / 取范围从 minmax+1 的随机数 :

    function randomInteger(min, max) {
      let rand = min + Math.random() * (max + 1 - min);
    	return Math.round(rand);
    }
    
    1
    2
    3
    4
  • 生成从 minmax 的随机数:

    function random(min, max) {
      return min + Math.random() * (max - min)
    }
    
    1
    2
    3
  • Math.max(a, b, c) / Math.min(a, b, c)Math.max() - JavaScript | MDN (mozilla.org) (opens new window) 获取一组参数中(参数可以是一组数字,可以是数组)的最大值或者最小值:

    console.log(Math.max(1, 3, 2));
    // expected output: 3
    
    console.log(Math.max(-1, -3, -2));
    // expected output: -1
    
    const array1 = [1, 3, 2];
    console.log(Math.max(...array1));
    // expected output: 3
    
    // 对于 Math.min() 使用方法同理。
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    如果有任一参数不能转换为数值,则返回值为 NaN 。如果没有参数,结果为 Infinity-Infinity )。

提示

使用 Math.max() 或者 Math.min() 裁剪值:使某个方法的返回值始终小于或者大于某个值。

🌰 例子:

var x = f(foo);
if (x > boundary) {
    x = boundary;
}
1
2
3
4

Copy to Clipboard

可以写成:

var x = Math.min(f(foo), boundary); // 只要返回值大于边界值就只返回边界值
1

# 数字类型总结

  • 对于很多零(或者有很多小数位)的数字可以使用科学计数法 e 简化表示数值。
  • 对于不同的数字系统(不同进制的数字),在前添加相关的前缀,十六进制( 0x ),八进制( 0o )和二进制( 0b ),即可以在系统中写入数字。
    • parseInt(str,base) :指定的 base 数字系统 ( 2 ≤ base ≤ 36 ) 转换为十进制整数。
    • num.toString(base) :将数字转换为在给定的 base 数字系统中的字符串(二进制转换为其他进制)。
  • 提取出带有单位的数字字符串:使用 parseInt/parseFloat 进行转换,在特殊字符之前停止读取转换。
  • 小数的舍入:使用 Math.floorMath.ceilMath.truncMath.roundnum.toFixed(precision) 进行舍入。
  • 使用小数会有损失数字精度的问题。
  • 更多的数值类型的数学函数来自 Math (opens new window) 对象。
📢 上次更新: 2022/09/02, 10:18:16