什么是JavaScript的最高整数值,一个数字可以去而不会失去精度?

这是由语言定义的吗?是否有定义的最大值?在不同的浏览器中是否不同?

626402 次浏览

>=ES6:

Number.MIN_SAFE_INTEGER;Number.MAX_SAFE_INTEGER;

<=ES5

的参考

Number.MAX_VALUE;Number.MIN_VALUE;

console.log('MIN_VALUE', Number.MIN_VALUE);console.log('MAX_VALUE', Number.MAX_VALUE);
console.log('MIN_SAFE_INTEGER', Number.MIN_SAFE_INTEGER); //ES6console.log('MAX_SAFE_INTEGER', Number.MAX_SAFE_INTEGER); //ES6

JavaScript有两种数字类型:#0#1

最常用的数字类型Number是64位浮点数IEEE 754

这种类型的最大精确整数值是#0,即:

  • 253-1或
  • +/-9,007,199,254,740,991,或
  • 九千万亿七万亿一百九十九亿二亿五千四百万七十四万九百九十一

从这个角度来看:一万亿字节是一个PB(或一千TB)。

在这种情况下,“安全”指的是准确表示整数并正确比较它们的能力。

从规范:

注意所有大小为0的正整数和负整数大于253可以在Number类型中表示(实际上,整数0有两个表示,+0和-0)。

要安全地使用大于此值的整数,您需要使用#0,它没有上限。

请注意,按位运算符和移位运算符对32位整数进行操作,因此在这种情况下,最大安全整数为231-1,或2,147,483,647。

const log = console.logvar x = 9007199254740992var y = -xlog(x == x + 1) // true !log(y == y - 1) // also true !
// Arithmetic operators work, but bitwise/shifts only operate on int32:log(x / 2)      // 4503599627370496log(x >> 1)     // 0log(x | 1)      // 1


关于数字9,007,199,254,740,992的技术说明:此值有一个精确的IEEE-754表示,您可以从变量中分配和读取此值,因此对于小于或等于此值的整数域中的非常仔细选择应用程序,您可以将其视为最大值。

在一般情况下,您必须将此IEEE-754值视为不精确,因为它是编码逻辑值9,007,199,254,740,992还是9,007,199,254,740,993是不明确的。

Firefox 3似乎对庞大的数字没有问题。

1e+200*1e+100将计算到1e+300。

Safari似乎也没有问题。(为了记录,如果其他人决定测试这个,这是在Mac上。)

除非我在一天中的这个时候失去了大脑,否则这比64位整数要大得多。

我用一个公式做了一个简单的测试,X-(X+1)=-1,我可以在Safari、Opera和Firefox(在OS X上测试)上使用的最大X值是9e15。这是我用于测试的代码:

javascript: alert(9e15-(9e15+1));

尝试:

maxInt = -1 >>> 1

在Firefox 3.6中,它是2^31-1。

它是253==9 007 199 254 740 992。这是因为Number作为浮点存储在52位尾数中。

最小值为-253

这让一些有趣的事情发生了

Math.pow(2, 53) == Math.pow(2, 53) + 1>> true

也可能是危险的:)

var MAX_INT = Math.pow(2, 53); // 9 007 199 254 740 992for (var i = MAX_INT; i < MAX_INT + 2; ++i) {// infinite loop}

进一步阅读:http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html

简短的回答是“这取决于”。

如果您在任何地方使用按位运算符(或者如果您指的是数组的长度),则范围为:

未签名:0…(-1>>>0)

签名:(-(-1>>>1)-1)…(-1>>>1)

(碰巧数组的按位运算符和最大长度仅限于32位整数。)

如果您不使用按位运算符或使用数组长度:

签名:(-Math.pow(2,53))…(+Math.pow(2,53))

这些限制是由“Number”类型的内部表示强加的,它通常对应于IEEE 754双精度浮点表示。(请注意,与典型的有符号整数不同,由于内部表示的特性,负限制的大小与正限制的大小相同,实际上包括 0!)

为了安全起见

var MAX_INT = 4294967295;

推理

我想我会很聪明,用更务实的方法找到x + 1 === x的值。

我的机器每秒只能计算1000万……所以我会在28.56年后给出最终的答案。

如果你等不了那么久我敢打赌

  • 你的大部分循环都不会运行28.56年
  • 9007199254740992 === Math.pow(2, 53) + 1已经足够了
  • 您应该坚持4294967295,这是Math.pow(2,32) - 1,以避免位移动的预期问题

查找x + 1 === x

(function () {"use strict";
var x = 0, start = new Date().valueOf();
while (x + 1 != x) {if (!(x % 10000000)) {console.log(x);}
x += 1}
console.log(x, new Date().valueOf() - start);}());

在GoogleChrome内置的javascript中,您可以在称为无穷大之前转到大约2^1024。

在JavaScript中,有一个数字叫做Infinity

示例:

(Infinity>100)=> true
// Also worth notingInfinity - 1 == Infinity=> true
Math.pow(2,1024) === Infinity=> true

这可能足以回答有关此主题的一些问题。

要用于按位运算的任何内容必须在0x80000000(-2147483648或-2^31)和0x7fffffff(2147483647或2^31-1)之间。

控制台会告诉您0x80000000等于+2147483648,但0x80000000&0x80000000等于-2147483648。

吉米的回答正确地表示连续的JavaScript整数频谱为-90071992547409929007199254740992(对不起9007199254740993,你可能认为你9007199254740993,但你错了!在下面或jsfiddle中演示)。

console.log(9007199254740993);

However, there is no answer that finds/proves this programatically (other than the one CoolAJ86 alluded to in his answer that would finish in 28.56 years ;), so here's a slightly more efficient way to do that (to be precise, it's more efficient by about 28.559999999968312 years :), along with a test fiddle:

/*** Checks if adding/subtracting one to/from a number yields the correct result.** @param number The number to test* @return true if you can add/subtract 1, false otherwise.*/var canAddSubtractOneFromNumber = function(number) {var numMinusOne = number - 1;var numPlusOne = number + 1;    
return ((number - numMinusOne) === 1) && ((number - numPlusOne) === -1);}
//Find the highest numbervar highestNumber = 3; //Start with an integer 1 or higher
//Get a number higher than the valid integer rangewhile (canAddSubtractOneFromNumber(highestNumber)) {highestNumber *= 2;}
//Find the lowest number you can't add/subtract 1 fromvar numToSubtract = highestNumber / 4;while (numToSubtract >= 1) {while (!canAddSubtractOneFromNumber(highestNumber - numToSubtract)) {highestNumber = highestNumber - numToSubtract;}    
numToSubtract /= 2;}
//And there was much rejoicing.  Yay.console.log('HighestNumber = ' + highestNumber);

Node.js和GoogleChrome似乎都使用1024位浮点值,因此:

Number.MAX_VALUE = 1.7976931348623157e+308

其他人可能已经给出了通用的答案,但我认为给出一个快速确定它的方法是个好主意:

for (var x = 2; x + 1 !== x; x *= 2);console.log(x);

这让我在30Chrome不到一毫秒的时间内9007199254740992。

它将测试2的幂,以找出当“添加”1时,哪个等于自己。

ECMAScript 6:

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;

我是这样写的:

var max_int = 0x20000000000000;var min_int = -0x20000000000000;(max_int + 1) === 0x20000000000000;  //true(max_int - 1) < 0x20000000000000;    //true

同为int32

var max_int32 =  0x80000000;var min_int32 = -0x80000000;

斯卡托写道:

要用于按位运算的任何内容都必须在0x80000000(-2147483648或-2^31)和0x7fffffff(2147483647或2^31-1)。

控制台会告诉你0x80000000等于+2147483648,但是0x80000000&0x80000000等于-2147483648

十六进制小数是无符号的正值,所以0x80000000=2147483648-这在数学上是正确的。如果你想让它成为一个有符号的值,你必须右移:0x80000000>>0=-2147483648。你也可以写1<<31。

许多早期的答案显示9007199254740992 === 9007199254740992 + 1真正,以验证9,007,199,254,740,991是最大和安全的整数。

但是如果我们继续积累:

input: 9007199254740992 + 1  output: 9007199254740992  // expected: 9007199254740993input: 9007199254740992 + 2  output: 9007199254740994  // expected: 9007199254740994input: 9007199254740992 + 3  output: 9007199254740996  // expected: 9007199254740995input: 9007199254740992 + 4  output: 9007199254740996  // expected: 9007199254740996

我们可以看到,在大于9,007,199,254,740,992的数字中,只有偶数是可表示

这是一个解释双精度64位二进制格式如何工作的条目。让我们看看如何使用这种二进制格式来保持(表示)9,007,199,254,740,992

使用一个简短的版本从4,503,599,627,370,496演示它:

  1 . 0000 ---- 0000  *  2^52            =>  1  0000 ---- 0000.|-- 52 bits --|    |exponent part|        |-- 52 bits --|

在箭头的左侧,我们有位值1和相邻的基点。通过消耗左侧的指数部分,基点向右移动52步。基点结束于最后,我们得到纯二进制的4503599627370496。

现在让我们继续以1递增小数部分,直到所有位都设置为1,等于十进制中的9,007,199,254,740,991

  1 . 0000 ---- 0000  *  2^52  =>  1  0000 ---- 0000.(+1)1 . 0000 ---- 0001  *  2^52  =>  1  0000 ---- 0001.(+1)1 . 0000 ---- 0010  *  2^52  =>  1  0000 ---- 0010.(+1)...1 . 1111 ---- 1111  *  2^52  =>  1  1111 ---- 1111.

因为64位双精度格式严格为小数部分分配52位,如果我们再加上1,就没有更多的位可用了,所以我们可以做的是将所有位设置回0,并操作指数部分:

  ┏━━▶ This bit is implicit and persistent.┃1 . 1111 ---- 1111  *  2^52      =>  1  1111 ---- 1111.|-- 52 bits --|                     |-- 52 bits --|
(+1)
1 . 0000 ---- 0000  *  2^52 * 2  =>  1  0000 ---- 0000. * 2|-- 52 bits --|                     |-- 52 bits --|(By consuming the 2^52, radixpoint has no way to go, butthere is still one 2 left inexponent part)=>  1 . 0000 ---- 0000  *  2^53|-- 52 bits --|

现在我们得到9,007,199,254,740,992,对于大于它的数字,格式只能处理2的增量,因为小数部分的每一个增量1最终都会乘以指数部分的左2。这就是为什么当数字大于9,007,199,254,740,992时,双精度64位二进制格式不能容纳奇数:

                            (consume 2^52 to move radix point to the end)1 . 0000 ---- 0001  *  2^53  =>  1  0000 ---- 0001.  *  2|-- 52 bits --|                 |-- 52 bits --|

按照这种模式,当数字大于9,007,199,254,740,992*2=18,014,398,509,481,984时,只能持有4倍的分数:

input: 18014398509481984 + 1  output: 18014398509481984  // expected: 18014398509481985input: 18014398509481984 + 2  output: 18014398509481984  // expected: 18014398509481986input: 18014398509481984 + 3  output: 18014398509481984  // expected: 18014398509481987input: 18014398509481984 + 4  output: 18014398509481988  // expected: 18014398509481988

[2 251 799 813 685 2484 503 599 627 370 496)之间的数字如何?

 1 . 0000 ---- 0001  *  2^51  =>  1 0000 ---- 000.1|-- 52 bits --|                |-- 52 bits  --|

二进制中的值0.1恰好是2^-1(=1/2)(=0.5)因此,当数字小于4,503,599,627,370,496(2^52)时,有一位可用于表示整数的1/2倍

input: 4503599627370495.5   output: 4503599627370495.5input: 4503599627370495.75  output: 4503599627370495.5            

小于2,251,799,813,685,248(2^51)

input: 2251799813685246.75   output: 2251799813685246.8  // expected: 2251799813685246.75input: 2251799813685246.25   output: 2251799813685246.2  // expected: 2251799813685246.25input: 2251799813685246.5    output: 2251799813685246.5/**Please note that if you try this yourself and, say, logthese numbers to the console, they will get rounded. JavaScriptrounds if the number of digits exceed 17. The valueis internally held correctly:*/            
input: 2251799813685246.25.toString(2)output: "111111111111111111111111111111111111111111111111110.01"input: 2251799813685246.75.toString(2)output: "111111111111111111111111111111111111111111111111110.11"input: 2251799813685246.78.toString(2)output: "111111111111111111111111111111111111111111111111110.11"

指数部分的可用范围是多少?由格式分配给它的11位。

维基百科(更多细节,去那里)

IEEE 754双浮点Format.svg

因此,要使指数部分为2^52,我们需要设置e=1075。

让我们到达来源

特性介绍

MAX_SAFE_INTEGER常量的值为9007199254740991(9,007,199,254,740,991或~9000万亿)。该数字背后的原因是JavaScript使用IEEE 754中指定的双精度浮点格式数字,并且只能安全地表示-(2^53 - 1)2^53 - 1之间的数字。

在这种情况下,安全是指准确表示整数并正确比较它们的能力。例如,Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2将评估为true,这在数学上是不正确的。有关更多信息,请参阅Number.is安全整数()

因为MAX_SAFE_INTEGER数量的静态属性,所以您始终将其用作Number.MAX_SAFE_INTEGER,而不是您创建的数量对象的属性。

浏览器兼容性

输入图片描述

JavaScript在ECMAScript 2020:BigInt中收到了一种新的数据类型。它引入了具有“n”后缀的数字文字,并允许任意精度:

var a = 123456789012345678901012345678901n;

当然,当这样的大整数(可能是无意的)被强制转换为数字数据类型时,精度仍然会丢失。

而且,显然,由于内存有限,总会有精度限制,并且为了分配必要的内存和对如此大的数字执行算术而花费时间。

例如,生成一个具有十万个十进制数字的数字,在完成之前会有明显的延迟:

console.log(BigInt("1".padEnd(100000,"0")) + 1n)

…但它奏效了。