什么是JavaScript的最高整数值,数字可以达到而不会丢失精度?

TAL*_*ama 923 javascript browser math cross-browser

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

Jim*_*mmy 841

+/- 9007199254740991

ECMA第8.5节 - 数字

注意,幅度不大于2 53的所有正整数和负整数都可以在Number类型中表示(实际上,整数0有两个表示,+ 0和-0).

它们是64位浮点值,最大精确积分值是2 53 -1,或9007199254740991.在ES6中,这被定义为Number.MAX_SAFE_INTEGER.

请注意,按位运算符和移位运算符以32位整数运算,因此在这种情况下,最大安全整数为2 31 -1或2147483647.


测试出来!

var x = 9007199254740992;
var y = -x;
x == x + 1; // true !
y == y - 1; // also true !
// Arithmetic operators work, but bitwise/shifts only operate on int32:
x / 2;      // 4503599627370496
x >> 1;     // 0
x | 1;      // 1
Run Code Online (Sandbox Code Playgroud)

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

在一般情况下,您必须将此IEEE-754值视为不精确,因为它是否编码逻辑值9007199254740992或9007199254740993是不明确的.

  • 这似乎是正确的,但是在某个地方定义了这个,álaC的MAX_INT或Java的Integer.MAX_VALUE? (73认同)
  • 9007199254740992实际上不是最大值,这里的最后一位已经假定为零,因此您丢失了1位精度.真正的安全号码是9007199254740991(Number.MAX_SAFE_INTEGER) (52认同)
  • `4294967295 === Math.pow(2,32) - 1;` (44认同)
  • 也许值得注意的是,javascript中没有实际的(int).Number的每个实例都是(float)或NaN. (37认同)
  • 那么我们可以用来确保精确度的最小和最大整数是多少? (13认同)
  • @TALlama在ES6中,将会有一个常量:`Number.MAX_SAFE_INTEGER`(已经在Chrome中登陆了实验性JS).现在这个答案很好. (6认同)
  • 根据IEEE_754标准,64位浮点使用53位作为尾数.至于javascript常量,我不知道任何. (5认同)
  • @ LucioM.Tato问题特别要求"不丢失精度"Number.MAX_VALUE是"最大值,句点",但例如,`Number.MAX_VALUE - 1`不是有效值. (5认同)
  • Number的每个实例都是(float)<stop here>"或NaN." ..."或+/-无穷大":冗余,IEEE-754(浮点)编码NaN和+/-无穷大作为格式的一部分.也是-0 (5认同)
  • @ Beetroot-Beetroot或+/-无限 (4认同)
  • 是Number.MAX_VALUE (4认同)
  • 顺便说一句,这个数字读作:九千万亿,七万亿,一百九十九亿,二百五十四百万,七十四万,九十九 (3认同)
  • @EvgeniNabokov:Number.MAX_VALUE是64位浮点数可表示的最大值.但是,64位浮点数不能表示小于1.79E + 308的所有整数(您可能知道,64位只能表示2 ^ 64 = 1.84E + 19个唯一值).Number.MAX_SAFE_VALUE或9007199254740991具有以下属性:每个正整数<= 9007199254740991可由64位浮点表示,而不会丢失精度.您可以在我的代码片段中看到,对于x大于9007199254740991的值,您可以使用`x == x + 1`,这意味着我们正在失去精度. (3认同)
  • 链接到规范:http://ecma262-5.com/ELS5_HTML.htm#Section_8.5 (2认同)
  • @TJCrowder,这只是“失去精度”的不同定义,我认为作为一般原则使用更不安全。值 9007199254740992 的 IEEE-754 表示形式与值 9007199254740993 的表示形式相同,因此如果您有此位模式,则必须将其视为不精确的值,因为您无法确定它是这两个值中的哪一个方法。 (2认同)

Pet*_*ley 456

> = ES6: __CODE__

<= ES5

来自参考: __CODE__

Number.MIN_SAFE_INTEGER;
Number.MAX_SAFE_INTEGER;
Run Code Online (Sandbox Code Playgroud)

  • ES6引入了`Number.MIN_SAFE_INTEGER`和`Number.MAX_SAFE_INTEGER` (33认同)
  • 我已经编辑了这个问题,想要了解最大整数值,而不仅仅是最大数值.对不起,这里有困惑. (22认同)
  • 请注意,`Number.MIN_VALUE`是可能的最小_positive_数字._least_值(即小于其他任何值)可能是`-Number.MAX_VALUE`. (7认同)
  • 返回的结果是否保证在所有浏览器上都相同? (5认同)
  • 这是最大的浮点值。问题是关于最高整数值。虽然 `Number.MAX_VALUE` 是一个整数,你不能在不损失精度的情况下超过 `2^53`。 (2认同)
  • 那么,在这种情况下,我们应该否决答案,因为更新的问题是错误的,还是因为 Peter Baily 在回答时是正确的而放弃? (2认同)
  • 目前的这个答案是不正确的,因为它暗示 MAX_SAFE_INTEGER 和 MAX_VALUE 都回答了这个问题,并且区别仅在于 ES 版本支持它们。事实上,只有 MAX_SAFE_INTEGER 正确回答了问题,只有 MAX_VALUE 回答了最初发布的问题。MIN_SAFE_INTEGER 和 MIN_VALUE 与精确问题无关。 (2认同)

Vje*_*eux 108

它是2 53 == 9 007 199 254 740 992.这是因为Numbers在52位尾数中存储为浮点数.

最小值为-2 53.

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

Math.pow(2, 53) == Math.pow(2, 53) + 1
>> true
Run Code Online (Sandbox Code Playgroud)

而且也可能很危险:)

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

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

  • @ninjagecko,他从MAX_INT开始,所以结束就在那里.同样使用i + = 1000000000会使它不再是无限循环.试试吧. (2认同)

Ban*_*eil 55

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

例子:

(Infinity>100)
=> true

// Also worth noting
Infinity - 1 == Infinity
=> true

Math.pow(2,1024) === Infinity
=> true
Run Code Online (Sandbox Code Playgroud)

对于有关此主题的一些问题,这可能就足够了.

  • 有些东西告诉我,无穷大不符合整数.:) (24认同)
  • 注意`Infinity - 1 === Infinity` (9认同)
  • 但是当你在寻找最小值时,初始化一个`min`变量就足够了. (7认同)
  • 也没有任何价值,它也可以处理负无限.所以`1 - Infinity === -Infinity` (5认同)
  • 也(无穷大<100)=>假和Math.pow(2,1024)===无穷大 (2认同)

Bri*_*y37 40

吉米的答案正确地表示连续的JavaScript整数谱为-90071992547409929007199254740992(抱歉9007199254740993,你可能认为你是9007199254740993,但你错了! 下面或jsfiddle的演示).

console.log(9007199254740993);
Run Code Online (Sandbox Code Playgroud)

然而,没有任何答案能够以编程方式找到/证明这一点(除了CoolAJ86在他的答案中提到的将在28.56 年内完成;),所以这里有一个更有效的方法(确切地说,它更有效率)通过约28.559999999968312年:),以及测试小提琴:

/**
 * 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 number
var highestNumber = 3; //Start with an integer 1 or higher

//Get a number higher than the valid integer range
while (canAddSubtractOneFromNumber(highestNumber)) {
    highestNumber *= 2;
}

//Find the lowest number you can't add/subtract 1 from
var 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);
Run Code Online (Sandbox Code Playgroud)

  • @ CoolAJ86:大声笑,我很期待2040年3月15日.如果我们的数字匹配,我们应该举办派对:) (7认同)

Coo*_*J86 32

为了安全起见

var MAX_INT = 4294967295;
Run Code Online (Sandbox Code Playgroud)

推理

我以为我会聪明一点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);
}());
Run Code Online (Sandbox Code Playgroud)

  • 你不能在2 ^ 53 - 2开始测试吗?(是的,你可以,我只是尝试过,即使-3是安全的:var x = Math.pow(2,53)-3; while(x!= x + 1)x ++;) - > 9007199254740991 (4认同)

Wai*_*ung 28

ECMAScript 6:

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;
Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;
Run Code Online (Sandbox Code Playgroud)

  • 请仔细阅读答案,我们没有使用ECMAScript 6中的Number.MAX_SAFE_INTEGER的默认实现,我们是通过Math.pow(2,53)-1定义的. (4认同)
  • 通过向后工作在所有浏览器中计算"MAX_SAFE_INTEGER"是否可靠?你应该向前移动吗?即,Number.MAX_SAFE_INTEGER = 2*(Math.pow(2,52) - 1)+ 1; (3认同)

dan*_*ton 27

简短的回答是"这取决于".

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

无符号: 0…(-1>>>0)

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

(碰巧的是,按位运算符和数组的最大长度限制为32位整数.)

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

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

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

  • 贬低谁,你是否愿意发表评论? (6认同)

Car*_*arr 16

之前的许多答案都显示了结果true,9007199254740992 === 9007199254740992 + 1
9 007 199 254 740 991是最大安全整数.

如果我们继续积累怎么办?

input: 9007199254740992 + 1  output: 9007199254740992  // expected: 9007199254740993
input: 9007199254740992 + 2  output: 9007199254740994  // expected: 9007199254740994
input: 9007199254740992 + 3  output: 9007199254740996  // expected: 9007199254740995
input: 9007199254740992 + 4  output: 9007199254740996  // expected: 9007199254740996
Run Code Online (Sandbox Code Playgroud)

我们可以发现,在大于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 --|
Run Code Online (Sandbox Code Playgroud)

在箭头的左侧,我们有位值1和一个相邻的小数点,然后通过相乘2^52,我们向右移动小数点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. 
Run Code Online (Sandbox Code Playgroud)

现在,因为在双精度64位二进制格式中,它严格地为分数分配52位,没有更多位可用于携带以再添加1,所以我们能做的是将所有位设置回0,并进行操作指数部分:

  |--> This bit is implicit and persistent.
  |        
  1 . 1111 ---- 1111  *  2^52      =>  1  1111 ---- 1111. 
     |-- 52 bits --|                     |-- 52 bits --|

                          (+1)
                                     (radix point has no way to go)
  1 . 0000 ---- 0000  *  2^52 * 2  =>  1  0000 ---- 0000. * 2  
     |-- 52 bits --|                     |-- 52 bits --|

  =>  1 . 0000 ---- 0000  *  2^53 
         |-- 52 bits --| 
Run Code Online (Sandbox Code Playgroud)

现在我们得到 9 007 199 254 740 992,并且数字大于它,格式可以容纳的是分数的2倍:

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

因此,当数字大于9 007 199 254 740 992*2 = 18 014 398 509 481 984时,只能保留4倍的分数:

input: 18014398509481984 + 1  output: 18014398509481984  // expected: 18014398509481985
input: 18014398509481984 + 2  output: 18014398509481984  // expected: 18014398509481986
input: 18014398509481984 + 3  output: 18014398509481984  // expected: 18014398509481987
input: 18014398509481984 + 4  output: 18014398509481988  // expected: 18014398509481988
Run Code Online (Sandbox Code Playgroud)

如何[之间号2 251 799 813 685 248,4 503 599 627 370 496)?

 1 . 0000 ---- 0001  *  2^51  =>  1 0000 ---- 000.1
     |-- 52 bits --|                |-- 52 bits  --|
Run Code Online (Sandbox Code Playgroud)

小数点后的位值1精确为2 ^ -1.(= 1/2,= 0.5)因此,当数字小于4 503 599 627 370 496(2 ^ 52)时,有一位可用于表示整数1/2倍:

input: 4503599627370495.5   output: 4503599627370495.5  
input: 4503599627370495.75  output: 4503599627370495.5  
Run Code Online (Sandbox Code Playgroud)

少于2 251 799 813 685 248(2 ^ 51)

input: 2251799813685246.75   output: 2251799813685246.8  // expected: 2251799813685246.75 
input: 2251799813685246.25   output: 2251799813685246.2  // expected: 2251799813685246.25 
input: 2251799813685246.5    output: 2251799813685246.5

// If the digits exceed 17, JavaScript round it to print it.
//, but the value is 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"
Run Code Online (Sandbox Code Playgroud)

指数部分的可用范围是多少?格式为它分配了11位.来自Wiki的完整格式:(有关详细信息,请访问)

IEEE 754双浮点格式.vg

在此输入图像描述

因此,要在指数部分获得2 ^ 52,我们需要设置e = 1075.


Phi*_*e97 11

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

for (var x = 2; x + 1 !== x; x *= 2);
console.log(x);
Run Code Online (Sandbox Code Playgroud)

在Chrome 30中,我在不到一毫秒的时间内就获得了9007199254740992.

它会测试2的幂来找到哪一个,当'加'1时,等于自己.


小智 7

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

控制台将告诉您0x80000000等于+2147483648,但0x80000000和0x80000000等于-2147483648.


tri*_*cot 6

在撰写本文时,JavaScript 正在接收一种新的数据类型:BigInt. 这是第 4 阶段的 TC39 提案,将包含在EcmaScript 2020 中BigInt适用于 Chrome 67+、FireFox 68+、Opera 54 和 Node 10.4.0。它正在 Safari 等人中进行......它引入了具有“n”后缀的数字文字并允许任意精度:

var a = 123456789012345678901012345678901n;
Run Code Online (Sandbox Code Playgroud)

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

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

例如,生成一个十万位十进制数字的数字,在完成之前需要明显的延迟:

console.log(BigInt("1".padEnd(100000,"0")) + 1n)
Run Code Online (Sandbox Code Playgroud)

...但它有效。


小智 5

尝试:

maxInt = -1 >>> 1
Run Code Online (Sandbox Code Playgroud)

在Firefox 3.6中它是2 ^ 31 - 1.

  • 按位操作在Javascript上运行32位. (12认同)
  • 2 ^ 31是用英语将第二个写入第三十一的力量.它不在代码块中.你会抱怨有人使用; 在一个答案中,因为这是一个在Javascript中具有不同含义的角​​色? (10认同)
  • 再一次,我一点也不困惑.我对_written_的内容进行了评论和评论.如果Math.pow()是意思,那就应该写.在回答有关JavaScript的问题时,使用不同语言的语法是不合适的.使用在JavaScript中有效的语法更加不合适,但JavaScript中的解释具有与预期不同的含义. (3认同)
  • @danorton:我不确定您了解自己在做什么。`^`意味着**提高了力量**。在JavaScript控制台中,“ ^”是** XOR **,不是引发 (2认同)
  • 打开Chrome / Firefox控制台。输入5 ^ 2。在二进制中,5是101,2是010。现在,如果对它们按位进行XOR,将得到`5(101)^ 2(010)= 7(111)`[如果您感到困惑,请仔细阅读](https://developer.mozilla.org/zh- US / docs / Web / JavaScript / Reference / Operators / Bitwise_Operators)这里讨论的是`Math.pow()`而不是`^`运算符 (2认同)
  • 即使一个*应该*用纯文本写**2³¹**而不是** 2 ^ 31 **也是常见的做法,因为大多数键盘布局默认情况下都没有这些字符。至少我对理解这个答案的含义没有任何问题。 (2认同)