JavaScript Number 数值类型详解与精度问题
整数和浮点数
JavaScript 内部,所有数字都是以 64 位浮点数形式储存,即使整数也是如此。所以,1 与 1.0 是相同的,是同一个数。 这就是说,JavaScript 语言的底层根本没有整数,所有数字都是小数(64 位浮点数)。
如果某些运算必须是整数才能完成,此时
JavaScript会自动把64位浮点数,转成 32 位整数,然后再进行运算
由于浮点数不是精确的值,涉及小数运算时得到的结果可能不是预期的
console.log(0.1 + 0.2 === 0.3);
// false
console.log(0.3 / 0.1);
// 2.9999999999999996
console.log(0.3 - 0.2 === 0.2 - 0.1);
// false
数值精度
JavaScript 浮点数的 64 个二进制位,从最左边开始,是这样组成的。
- 第
1位:符号位,0表示正数,1表示负数 - 第
2位到第12位(共11位):指数部分 - 第
13位到第64位(共52位):小数部分(即有效数字)
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。
精度最多只能到 53 个二进制位,这意味着,绝对值小于 2 的 53 次方的整数,即 -2^53 到 2^53,都可以精确表示。
下面代码中,大于 2 的 53 次方以后,整数运算的结果开始出现错误。所以,大于 2 的 53 次方的数值,都无法保持精度
Math.pow(2, 53);
// 9007199254740992
Math.pow(2, 53) + 1;
// 9007199254740992
Math.pow(2, 53) + 2;
// 9007199254740994
Math.pow(2, 53) + 3;
// 9007199254740996
Math.pow(2, 53) + 4;
// 9007199254740996
数值范围
根据标准,64 位浮点数的指数部分的长度是 11 个二进制位,意味着指数部分的最大值是 2047(2 的 11 次方减 1)。 也就是说,64 位浮点数的指数部分的值最大为 2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为 2^1024 到 2^1023(开区间),超出这个范围的数无法表示。
如果一个数大于等于 2 的 1024 次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回 Infinity。
Math.pow(2, 1024); // Infinity
如果一个数小于等于 2 的 -1075 次方(指数部分最小值 -1023,再加上小数部分的 52 位),那么就会发生为“负向溢出”, 即 JavaScript 无法表示这么小的数,这时会直接返回 0。
Math.pow(2, -1075); // 0
示例
var x = 0.5;
for (var i = 0; i < 25; i++) {
x = x * x;
}
x; // 0
数值的表示法
科学计数法表示: 允许字母 e 或 E 的后面,跟着一个整数,表示这个数值的指数部分
123e3; // 123000
123e-3 - // 0.123
3.1e2 - // -310
3.29e3; // -3290
某些情况下 JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。
小数点前的数字多于 21 位。
1234567890123456789012;
// 1.2345678901234568e+21
123456789012345678901;
// 123456789012345680000
小数点后的零多于 5 个。
// 小数点后紧跟5个以上的零,
// 就自动转为科学计数法
0.0000003; // 3e-7
// 否则,就保持原来的字面形式
0.000003; // 0.000003
数值的进制
- 十进制:没有前导
0的数值。 - 八进制:有前缀
0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。 - 十六进制:有前缀
0x或0X的数值。 - 二进制:有前缀
0b或0B的数值。
默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制。下面是一些例子。
0xff; // 255
0o377; // 255
0b11; // 3
如果八进制、十六进制、二进制的数值里面,出现不属于该进制的数字,就会报错。
// 0xzz // 报错
// 0o88 // 报错
// 0b22 // 报错
**通常来说,有前导 0 的数值会被视为八进制,但是如果前导 0 后面有数字 8 和 9,则该数值被视为十进制 **
0;
888; // 888
0777; // 511
+0 与 -0
区别就是 64 位浮点数表示法的符号位不同。它们是等价的。
-0 === +0; // true
0 === -0; // true
0 === +0; // true
console.log(JSON.stringify(-0)); // 0
console.log(JSON.parse(-0)); // 0
唯一有区别的场合是,+0 或 -0 当作分母,返回的值是不相等的。
1 / +0 === 1 / -0; // false
上面的代码之所以出现这样结果,是因为除以正零得到 +Infinity,除以负零得到 -Infinity,这两者是不相等的
Number 对象
Number 对象是数值对应的包装对象,可以作为构造函数使用,也可以作为工具函数使用。
在浏览器打印出来的原型属性

作为构造函数时,它用于生成值为数值的对象。
var n = new Number(1);
typeof n; // "object"
作为工具函数时,它可以将任何类型的值转为数值。
Number(true); // 1
- 如果参数无法被转换为数字,则返回
NaN。 - 在非构造器上下文中 (如:没有
new操作符),Number能被用来执行类型转换。 - 整数类型的范围
-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个整数。
JavaScript 的 Number 对象是经过封装的能让你处理数字值的对象。Number 对象由 Number() 构造器创建。
new Number(value);
var a = new Number('123'); // a === 123 is false
var b = Number('123'); // b === 123 is true
a instanceof Number; // is true
b instanceof Number; // is false
Number 的静态属性
Number 对象拥有以下一些静态属性(即直接定义在 Number 对象上的属性,而不是定义在实例上的属性)。
Number.MAX_VALUE属性表示在JavaScript里所能表示的最大数值。值接近于1.79E+308。大于MAX_VALUE的值代表 "Infinity"。Number.MIN_VALUE属性表示在JavaScript中所能表示的最小的正值。是JavaScript里最接近0的正值,而不是最小的负值。约为5e-324。小于MIN_VALUE的值将会转换为0。Number.NEGATIVE_INFINITY属性表示负无穷大。Number.NEGATIVE_INFINITY的值和全局对象的Infinity属性的负值相同- 该值的行为同数学上的无穷大(
infinity)有一点儿不同: - 1、任何正值,包括
POSITIVE_INFINITY,乘以NEGATIVE_INFINITY为NEGATIVE_INFINITY。 - 2、任何负值,包括
NEGATIVE_INFINITY,乘以NEGATIVE_INFINITY为POSITIVE_INFINITY。 - 3、
0乘以NEGATIVE_INFINITY为NaN. - 4、
NaN乘以NEGATIVE_INFINITY为NaN. - 5、
NEGATIVE_INFINITY除以任何负值(除了NEGATIVE_INFINITY)为POSITIVE_INFINITY。 - 6、
NEGATIVE_INFINITY除以任何正值(除了POSITIVE_INFINITY)为NEGATIVE_INFINITY。 - 7、
NEGATIVE_INFINITY除以NEGATIVE_INFINITY或POSITIVE_INFINITY是NaN。 - 8、任何数除以
NEGATIVE_INFINITY为0。
- 该值的行为同数学上的无穷大(
Number.POSITIVE_INFINITY属性表示正无穷大。Number.POSITIVE_INFINITY的值同全局对象Infinity属性的值相同。- 该值的表现同数学上的无穷大有点儿不同:
- 1、任何正值,包括
POSITIVE_INFINITY,乘以POSITIVE_INFINITY为POSITIVE_INFINITY。 - 2、任何负值,包括
NEGATIVE_INFINITY,乘以POSITIVE_INFINITY为NEGATIVE_INFINITY。 - 3、
0乘以POSITIVE_INFINITY为NaN。 - 4、
NaN乘以POSITIVE_INFINITY为NaN。 - 5、
POSITIVE_INFINITY除以NEGATIVE_INFINITY以外的任何负值为NEGATIVE_INFINITY。 - 6、
POSITIVE_INFINITY除以POSITIVE_INFINITY以外的任何正值为POSITIVE_INFINITY。 - 7、
POSITIVE_INFINITY除以NEGATIVE_INFINITY或POSITIVE_INFINITY为NaN。 - 8、任何数除以
POSITIVE_INFINITY为0。
Number.NaN表示“非数字”(Not-A-Number)。和NaN相同。
console.log(Number.NEGATIVE_INFINITY); // -Infinity
console.log(-Number.NEGATIVE_INFINITY); // Infinity
console.log(Number.NEGATIVE_INFINITY + 100); // -Infinity
console.log(typeof Number.NEGATIVE_INFINITY); // number
console.log(Number.POSITIVE_INFINITY * Number.NEGATIVE_INFINITY); // -Infinity
console.log(Number.NEGATIVE_INFINITY * Number.NEGATIVE_INFINITY); // Infinity
console.log(0 * Number.NEGATIVE_INFINITY); // NaN
console.log(0 / Number.NEGATIVE_INFINITY); // -0
console.log(0 - Number.NEGATIVE_INFINITY); // Infinity
console.log(-1 - Number.NEGATIVE_INFINITY); // Infinity
console.log(Number.NaN / Number.NEGATIVE_INFINITY); // NaN
console.log(Number.NaN * Number.NEGATIVE_INFINITY); // NaN
console.log(Number.NaN + Number.NEGATIVE_INFINITY); // NaN
console.log(Number.NEGATIVE_INFINITY / Number.NaN); // NaN
console.log(Number.NEGATIVE_INFINITY * Number.NaN); // NaN
console.log(Number.NEGATIVE_INFINITY + Number.NaN); // NaN
console.log(Number.NEGATIVE_INFINITY / Number.NEGATIVE_INFINITY); // NaN
console.log(Number.NEGATIVE_INFINITY * Number.NEGATIVE_INFINITY); // Infinity
console.log(Number.NEGATIVE_INFINITY / -100); // Infinity
console.log(Number.NEGATIVE_INFINITY / Number.POSITIVE_INFINITY); // NaN
console.log(Number.NEGATIVE_INFINITY * Number.POSITIVE_INFINITY); // -Infinity
console.log(Number.NEGATIVE_INFINITY / 100); // -Infinity
console.log(1 / Number.NEGATIVE_INFINITY); // -0
Number 的一些实例方法
Number.prototype.toExponential(fractionDigits)
以指数表示法返回该数值字符串表示形式。
fractionDigits:一个整数,用来指定小数点后有几位数字。默认情况下用尽可能多的位数来显示数字。- 对数值字面量使用
toExponential()方法,且该数值没有小数点和指数时,应该在该数值与该方法之间隔开一个空格,以避免点号被解释为一个小数点。也可以使用两个点号调用该方法。 - 如果一个数值的小数位数多余
fractionDigits参数所提供的,则该数值将会在fractionDigits指定的小数位数处四舍五入。 - 如果
fractionDigits太小或太大将会抛出该错误。介于0和20(包括20)之间的值不会引起RangeError。 执行环境也可以支持更大或更小范围。
var numObj = 77.1234;
console.log('numObj.toExponential() is ' + numObj.toExponential()); // 输出 7.71234e+1
console.log('numObj.toExponential(4) is ' + numObj.toExponential(4)); // 输出 7.7123e+1
console.log('numObj.toExponential(2) is ' + numObj.toExponential(2)); // 输出 7.71e+1
console.log('77.1234.toExponential() is ' + (77.1234).toExponential()); // 输出 7.71234e+1
console.log('77.toExponential() is ' + (77).toExponential()); // 输出 7.7e+1
Number.prototype.toFixed(digits)
- 使用定点表示法来格式化一个数值。该数值在必要时进行四舍五入,另外在必要时会用
0来填充小数部分,以便小数部分有指定的位数。 digits:小数点后数字的个数;介于0到20(包括)之间,实现环境可能支持更大范围。如果忽略该参数,则默认为0。- 如果
digits参数太小或太大。0到20(包括)之间的值不会引起RangeError。实现环境(implementations)也可以支持更大或更小的值
返回值:使用定点表示法表示给定数字的字符串。
如果数值大于 1e+21,该方法会简单调用 Number.prototype.toString() 并返回一个指数记数法格式的字符串。
var numObj = 12345.6789;
numObj.toFixed(); // 返回 "12346":进行四舍五入,不包括小数部分
numObj.toFixed(1); // 返回 "12345.7":进行四舍五入
numObj.toFixed(6); // 返回 "12345.678900":用0填充
(1.23e20).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" (若用括号提高优先级,则返回字符串)
由于浮点数的原因,小数 5 的四舍五入是不确定的
(10.055)
.toFixed(2)(
// 10.05
10.005,
)
.toFixed(2); // 10.01
重写类似 toFixed 方法
function toFixed(num, s) {
var rateNum = Math.pow(10, s);
var des = num * rateNum + 0.5;
des = parseInt(des, 10) / rateNum;
return des + '';
}
Number.prototype.toPrecision(precision)
- 以指定的精度返回该数值对象的字符串表示。
- 1、
precision:可选。一个用来指定有效数个数的整数。 - 2、以定点表示法或指数表示法表示的一个数值对象的字符串表示,四舍五入到
precision参数指定的显示数字位数。 - 3、如果忽略
precision参数,则该方法表现类似于Number.prototype.toString()。如果该参数是一个非整数值,将会向下舍入到最接近的整数。 - 4、如果
precison参数不在1和100(包括)之间,将会抛出一个RangeError。执行环境也可以支持更大或更小的范围。ECMA-262只需要最多21位显示数字。
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"
该方法用于四舍五入时不太可靠,跟浮点数不是精确储存有关。
(12.35)
.toPrecision(3)(
// "12.3"
12.25,
)
.toPrecision(3)(
// "12.3"
12.15,
)
.toPrecision(3)(
// "12.2"
12.45,
)
.toPrecision(3); // "12.4"
Number.prototype.toString(radix)
返回指定 Number 对象的字符串表示形式。
radix: 指定要用于数字到字符串的转换的基数(从2到36)。如果未指定radix参数,则默认值为10。- 如果
toString()的radix参数不在2到36之间,将会抛出一个RangeError。 Number对象覆盖了Object对象上的toString()方法,它不是继承的Object.prototype.toString()。对于Number对象,toString()方法以指定的基数返回该对象的字符串表示。
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'
为什么要带小括号:如果不加括号,这个点会被 JavaScript 引擎解释成小数点,从而报错。
console.log(926.
toString()
)
; // Invalid or unexpected token
console.log((926).toString()); // 926
// JavaScript 会把第一个点理解成小数点(即10.0),把第二个点理解成调用对象属性
console.log(926..toString()); // 926
console.log(926.
toString()
)
; // 926
console.log(926.0.toString()); // 926
Number 的一些静态方法
Number.isFinite()
- 用来检测传入的参数是否是一个有穷数(
finite number)。 - 返回值:一个布尔值,表示给定的值是否是一个有穷数。
- 和全局的
isFinite()函数相比,这个方法不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回true。
Number.isFinite(Infinity); // false
Number.isFinite(NaN); // false
Number.isFinite(-Infinity); // false
Number.isFinite(0); // true
Number.isFinite(2e64); // true
Number.isFinite('0'); // false, 全局函数 isFinite('0') 会返回 true
Number.isFinite(null); // false
// 全局的
isFinite(Infinity); // false
isFinite(NaN); // false
isFinite(-Infinity); // false
isFinite(0); // true
isFinite(2e64); // true
isFinite('0'); // true, 在 Number.isFinite('0')中将会得到false
isFinite(null); // true Number(null) == 0 返回的是true
isFinite(false); // true
Number.isInteger()
- 用来判断给定的参数是否为整数。
- 如果被检测的值是整数,则返回
true,否则返回false。注意NaN和正负Infinity不是整数.
Number.isInteger(-100000); // true
Number.isInteger(0.1); // false
Number.isInteger(Math.PI); // false
Number.isInteger(Infinity); // false
Number.isInteger('10'); // false
Number.isInteger(false); // false
Number.isInteger([1]); // false
JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
Number.isInteger(25); // true
Number.isInteger(25.0); // true
Number.isNaN()
Number.isSafeInteger()
- 用来判断传入的参数值是否是一个“安全整数”
- 安全整数范围为
-(253 - 1)到253 - 1之间的整数,包含-(253 - 1)和253 - 1。
Number.isSafeInteger(3); // true
Number.isSafeInteger(Math.pow(2, 53)); // false
Number.isSafeInteger(Math.pow(2, 53) - 1); // true
Number.isSafeInteger(NaN); // false
Number.isSafeInteger(Infinity); // false
Number.isSafeInteger('3'); // false
Number.isSafeInteger(3.1); // false
Number.isSafeInteger(3.0); // true
Number.EPSILON
Number.EPSILON: 它表示 1 与大于 1 的最小浮点数之间的差。
- 对于
64位浮点数来说,大于1的最小浮点数相当于二进制的1.00..001,小数点后面有连续51个零。这个值减去1之后,就等于2的-52次方。 Number.EPSILON实际上是JavaScript能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。
作用 引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 - 0.3); // 5.551115123125783e-17
解决方法,如下代码如果两个数之间的差值小于 Number.EPSILON,我们就认为这两个值是相等的
function numbersCloseEnoughToEqual(n1, n2) {
return Math.abs(n1 - n2) < Number.EPSILON;
}
console.log(numbersCloseEnoughToEqual(0.1 + 0.2, 0.3)); // true;
一些特殊用法
- 数字前面的
0可以省略:
var a = 0.926;
var b = 0.926;
console.log(a === b); // true
- 小数点后小数部分最后面的
0也可以省略:
var a = 29.0;
var b = 29;
console.log(a === b); // true
数值分隔符
ES2021,允许 JavaScript 的数值使用下划线(_)作为分隔符。
let budget = 1_000_000_000_000;
budget === 10 ** 12; // true
123_00 === 12_300; // true
12345_00 === 123_4500; // true
12345_00 === 1_234_500; // true
1234500 === 1_2_3_4_5_0_0; // true
0.000_221 === 0.000221;
使用注意点
- 不能放在数值的最前面(
leading)或最后面(trailing)。 - 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的 e 或 E 前后不能有分隔符。
// 全部报错
3;
_;
0.141;
3;
_141;
1;
_e12;
1;
e_12;
123;
__456;
_1464301;
1464301;
_;
下面三个将字符串转成数值的函数,不支持数值分隔符
Number()parseInt()parseFloat()
Number('123_456'); // NaN
parseInt('123_456'); // 123

还没有留言,来发表第一条吧~