Void-7's

JS基础:数字类型

js

数字类型

现代JS中,number有两种类型:

  1. JS中常规数字以64位的格式IEEE-754存储 ,也被称为“双精度浮点数"。我们大多数时候使用的数字也就是这种类型。
  2. BigInt数字,用于表示任意长度的整数。有时我们会需要它。因为常规数字不能超过2^53或者小于-2^53。

编写数字的更多方法

  • 10亿可以直接写成1bn。(1 billion)

  • 科学计数 比如1e9表示1 billion

  • 十六进制 八进制 二进制的前缀分别是0x 0o 0b

  • 不同进制数字系统的函数:toString(base)

    let num = 255;
    
    alert( num.toString(16) );  // ff
    alert( num.toString(2) );   // 11111111
    
    alert( 123456..toString(36) ); // 2n9c
    

    JavaScript

舍入

几个对数字进行舍入的内建函数:

  • Math.floor

向下舍入:3.1 变成 3-1.1 变成 -2

  • Math.ceil

向上舍入:3.1 变成 4-1.1 变成 -1

  • Math.round

向最近的整数舍入:3.1 变成 33.6 变成 4-1.1 变成 -1

  • Math.trunc(IE 浏览器不支持这个方法)

移除小数点后的所有内容而没有舍入:3.1 变成 3-1.1 变成 -1

有两种方式可以实现这个需求:

  1. 乘除法

例如,要将数字舍入到小数点后两位,我们可以将数字乘以 100(或更大的 10 的整数次幂),调用舍入函数,然后再将其除回。

let num = 1.23456;

alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
  1. 函数 toFixed(n) 将数字舍入到小数点后 n 位,并以字符串形式返回结果。
let num = 12.34;
alert( num.toFixed(1) ); // "12.3"

这会向上或向下舍入到最接近的值,类似于 Math.round

let num = 12.36;
alert( num.toFixed(1) ); // "12.4"

请注意 toFixed 的结果是一个字符串。如果小数部分比所需要的短,则在结尾添加零:

let num = 12.34;
alert( num.toFixed(5) ); // "12.34000",在结尾添加了 0,以达到小数点后五位

我们可以使用一元加号或 Number() 调用,将其转换为数字:+ num.toFixed(5)

误差

二进制数字系统与我们日常中使用广泛的十进制数字系统注定存在无法精确转换的情况:比如0.1在二进制的形式中无法找到确切的对应,它是个无限循环的小数

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

我们可以看到:

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

当我们对两个数字进行求和时,它们的“精度损失”会叠加起来。

这就是为什么 0.1 + 0.2 不等于 0.3

我们能解决这个问题吗?当然,最可靠的方法是借助方法 toFixed(n) 对结果进行舍入:

let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30

请注意,toFixed 总是返回一个字符串。它确保小数点后有 2 位数字。如果我们有一个电子购物网站,并需要显示 ¥ 0.30,这实际上很方便。对于其他情况,我们可以使用一元加号将其强制转换为一个数字:

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

因此,乘/除法可以减少误差,但不能完全消除误差。

有时候我们可以尝试完全避免小数。例如,我们正在创建一个电子购物网站,那么我们可以用角而不是元来存储价格。但是,如果我们要打 30% 的折扣呢?实际上,完全避免小数处理几乎是不可能的。只需要在必要时剪掉其“尾巴”来对其进行舍入即可

isFinite和isNaN

  • Infinity(和 -Infinity)是一个特殊的数值,比任何数值都大(小)。
  • NaN 代表一个 error。

值 “NaN” 是独一无二的,它不等于任何东西,包括它自身,所以我们不能直接用===NaN比较

isFinite(value) 将其参数转换为数字,如果是常规数字,则返回 true,而不是 NaN/Infinity/-Infinity

alert( isFinite("15") ); // true
alert( isFinite("str") ); // false,因为是一个特殊的值:NaN
alert( isFinite(Infinity) ); // false,因为是一个特殊的值:Infinity

有时 isFinite 被用于验证字符串值是否为常规数字

let num = +prompt("Enter a number", '');

// 结果会是 true,除非你输入的是 Infinity、-Infinity 或不是数字
alert( isFinite(num) );

注意,在所有数字函数中,包括 isFinite,空字符串或仅有空格的字符串均被视为 0

Object.is 进行比较

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

  1. 它适用于 NaNObject.is(NaN,NaN)=== true,这是件好事。
  2. 0-0 是不同的:Object.is(0,-0)=== false,从技术上讲这是对的,因为在内部,数字的符号位可能会不同,即使其他所有位均为零。

在所有其他情况下,Object.is(a,b)a === b 相同。

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

parseInt 和 parseFloat

使用加号 +Number() 的数字转换是严格的。如果一个值不完全是一个数字,就会失败:

alert( +"100px" ); // NaN

现实生活中,我们经常会有带有单位的值,例如 CSS 中的 "100px""12pt"。并且,在很多国家,货币符号是紧随金额之后的,所以我们有 "19€",并希望从中提取出一个数值。

这就是 parseIntparseFloat 的作用。

它们可以从字符串中“读取”数字,直到无法读取为止。如果发生 error,则返回收集到的数字。函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数:

alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5

alert( parseInt('12.3') ); // 12,只有整数部分被返回了
alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取

某些情况下,parseInt/parseFloat 会返回 NaN。当没有数字可读时会发生这种情况:

alert( parseInt('a123') ); // NaN,第一个符号停止了读取

JavaScript

parseInt(str, radix) 的第二个参数

parseInt() 函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串:

alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效

alert( parseInt('2n9c', 36) ); // 123456

其他数学函数

JS内建Math对象包含了一个小型的数学函数和常量库。

比如:

Math.random()

返回一个从 0 到 1 的随机数(不包括 1)

alert( Math.random() ); // 0.1234567894322
alert( Math.random() ); // 0.5435252343232
alert( Math.random() ); // ... (任何随机数)
Math.max(a, b, c...)` / `Math.min(a, b, c...)

从任意数量的参数中返回最大/最小值。

alert( Math.max(3, 5, -10, 0, 1) ); // 5
alert( Math.min(1, 2) ); // 1
Math.pow(n, power)

返回 n 的给定(power)次幂

alert( Math.pow(2, 10) ); // 2 的 10 次幂 = 1024

总结

要写有很多零的数字:

  • "e" 和 0 的数量附加到数字后。就像:123e6123 后面接 6 个 0 相同。
  • "e" 后面的负数将使数字除以 1 后面接着给定数量的零的数字。例如 123e-6 表示 0.000123123 的百万分之一)。

对于不同的数字系统:

  • 可以直接在十六进制(0x),八进制(0o)和二进制(0b)系统中写入数字。
  • parseInt(str,base) 将字符串 str 解析为在给定的 base 数字系统中的整数,2 ≤ base ≤ 36
  • num.toString(base) 将数字转换为在给定的 base 数字系统中的字符串。

要将 12pt100px 之类的值转换为数字:

  • 使用 parseInt/parseFloat 进行“软”转换,它从字符串中读取数字,然后返回在发生 error 前可以读取到的值。

小数:

  • 使用 Math.floorMath.ceilMath.truncMath.roundnum.toFixed(precision) 进行舍入。
  • 请确保记住使用小数时会损失精度。

更多数学函数:

  • 需要时请查看 Math 对象。这个库很小,但是可以满足基本的需求。