为什么 JavaScript 中带小数的最大位数只有 16

不久前我在测试一些 HTML 表单时遇到了这个问题.JavaScript Number 中带小数点的最大位数仅为 16

I came across this issue a while back when I was testing some HTML forms. The Maximum number of digits in JavaScript Number with a decimal point is only 16

我已经尝试了以下

var x = 12345678912345.6789

x 是 12345678912345.68 - 只有 16 位数字

x is 12345678912345.68 - 16 Digits only

var x = 123456789123.6789

x 是 123456789123.6789 - 只有 16 位数字

x is 123456789123.6789 - 16 Digits only

new Number(12345678912345.6789)

12345678912345.68 - 只有 16 位数字

12345678912345.68 - 16 Digits only

new Number(123456789123.6789)

123456789123.6789 - 只有 16 位数字

123456789123.6789 - 16 Digits only

如果计算总位数,则为 16.如果增加小数点前的位数,则小数点后的位数会四舍五入

if you count the total digits, they are 16. If you increase digits before decimal point, the digits after decimal point get rounded

同样

new Number(.12345678912367890)

为 0.1234567891236789 - 仅 16 位(注意缺少尾随 0)

is 0.1234567891236789 - 16 Digits only (notice trailing 0 is missing)

这让我推断我只能有 16 位数字,其中包含小数.如果我尝试添加更多数字,则数字开始舍入

Which makes me deduce that I can only have 16 Digits in a number with Decimal in it. If I try to add more digits, the number starts to round

我还观察到,当我在 ASP.NET MVC 中将带小数的数字序列化为 JSoN 时,它还会将该数字转换为 Max 16 Digits 并将其余部分四舍五入

I also observed that when I serialize a number with decimal into JSoN in ASP.NET MVC, it also converts the number to Max 16 Digits and rounds the rest

我的问题:为什么?

推荐答案

根据 JS/ECMAScript 规范,Number type 使用 64 位格式 (binary64) 的双精度浮点数,由一个符号位(确定正值或负值)、11 个指数位和 52 个小数位(每个数字代表 4 位,因此 64 位有 16 位):

According to JS/ECMAScript specification, the Number type uses double-precision floating point which has 64-bit format (binary64), consists of a sign bit (determines positive or negative value), 11 exponent bits and 52 fraction bits (each digit represents 4-bits, hence 64-bit has 16 digits):

表示 双精度 64 位格式 IEEE 的 Number 类型IEEE 二进制标准中指定的 754-2008 值浮点运算.

使用双精度可以正确表示的最大正数是9007199254740992,这可以通过使用Math.pow(2, 53)来实现.如果数字范围介于 Math.pow(2, 53)Math.pow(2, 54) 之间(或介于 Math.pow(2, -53)Math.pow(2, -54)),只有偶数可以正确表示,因为指数位会影响小数位上的 LSB(最低有效位).

The maximum positive number which can be represented properly using double precision is 9007199254740992, which can be achieved by using Math.pow(2, 53). If the number range is between Math.pow(2, 53) and Math.pow(2, 54) (or between Math.pow(2, -53) and Math.pow(2, -54)), only even numbers can be represented properly because the exponent bits will affect LSB (least-significant bit) on the fraction bits.

让我们回顾一下大数部分:

Let's review the large number part:

var x = 12345678912345.6789

var x = new Number(12345678912345.6789)

这个数字包含超过 52 个小数位(总共 72 位),因此用于将小数位保持为 52 的舍入.

This number contains more than 52 fractional bits (72 bits in total), hence the rounding used to keep the fractional bits to 52.

还有这个十进制数:

var x = new Number(.12345678912367890)

这个数字包含 68 个小数位,因此最后一个零被切掉以保持 64 位长度.

This number contains 68 fractional bits, hence the last zero is chopped off to keep 64-bit length.

通常大于 9007199254740992 或小于 1.1102230246251565E-16 的数字表示形式存储为文字字符串,而不是 Number.如果您需要计算非常大的数字,可以使用某些外部库来执行任意精度算术.

Usually numeric representation larger than 9007199254740992 or smaller than 1.1102230246251565E-16 are stored as literal strings instead of Number. If you need to compute very large numbers, there are certain external libraries available to perform arbitrary precision arithmetic.

进一步阅读:

ECMAScript:数字编码

ECMAScript:处理大整数

相关文章