【问题标题】:What is JavaScript's highest integer value that a number can go to without losing precision?一个数字在不丢失精度的情况下可以达到的 JavaScript 最高整数值是多少?
【发布时间】:2022-11-30 03:32:30
【问题描述】:

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

【问题讨论】:

  • 您不需要依赖 JS 对 github.com/MikeMcl/big.js 等库的限制,请参见例如here for its reliability tests
  • big.js 可以使用的最大整数值是多少?
  • @DmitriZaitsev 我们不再需要依赖外部库(至少在某些浏览器上)。 1n << 10000n 是一个非常非常大的整数,没有损失任何精度,不需要任何依赖(不用说,甚至不接近极限)。
  • @DmitriZaitsev 注意 n 后缀。 BigInt 类是 ES2020 规范草案的一部分,已经在大多数浏览器中实现;您可以尝试评估它,例如Chrome 或 Firefox,没有外部库,并获得一个 3011 位的 BigInt。
  • @DmitriZaitsev:是的,它仅适用于整数。这个问题是关于整数的。

标签: javascript math browser cross-browser


【解决方案1】:

JavaScript 有两种数字类型:Number 和BigInt。

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

该类型的最大精确整数值为Number.MAX_SAFE_INTEGER,即:

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

换个角度来看:一千万亿字节就是一拍字节(或一千兆字节)。

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

From the spec:

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

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

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

const log = console.log
var x = 9007199254740992
var y = -x
log(x == x + 1) // true !
log(y == y - 1) // also true !

// Arithmetic operators work, but bitwise/shifts only operate on int32:
log(x / 2)      // 4503599627370496
log(x >> 1)     // 0
log(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 是不明确的。

【讨论】:

  • 这似乎是正确的,但是是否有某个地方定义了它,如 C 的 MAX_INT 或 Java 的 Integer.MAX_VALUE?
  • 4294967295 === Math.pow(2,32) - 1;
  • 那么我们可以用来确保精确度的最小和最大整数是多少?
  • 也许值得注意的是,javascript 中没有 actual (int)。 Number 的每个实例都是 (float) 或 NaN。
  • 9007199254740992 并不是真正的最大值,此处的最后一位已假定为零,因此您丢失了 1 位精度。真正的安全号码是 9007199254740991 ( Number.MAX_SAFE_INTEGER )
【解决方案2】:

>= ES6:

Number.MIN_SAFE_INTEGER;
Number.MAX_SAFE_INTEGER;

<= ES5

来自the reference:

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); //ES6
console.log('MAX_SAFE_INTEGER', Number.MAX_SAFE_INTEGER); //ES6

【讨论】:

  • 我已经编辑了问题,以便更精确地了解最大整数值,而不仅仅是最大数字值。抱歉,在这里造成混乱。
  • 返回的结果是否保证在所有浏览器上都相同?
  • 请注意,Number.MIN_VALUE 是最小的可能积极的数字。这至少值(即小于其他任何值)可能是-Number.MAX_VALUE。
  • 这是最大浮点值。问题是关于最高整数值。虽然 Number.MAX_VALUE 是一个整数,但您不能在不损失精度的情况下超过 2^53。
  • ES6 引入了Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER
【解决方案3】:

这是253== 9 007 199 254 740 992。这是因为Numbers 存储为 52 位尾数中的浮点数。

最小值为 -253.

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

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

也可能很危险 :)

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
}

延伸阅读:http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html

【讨论】:

  • 尽管在合理的时间范围内永远不会到达 for 循环的末尾,但您可能希望说 i += 1000000000
  • @ninjagecko,他从 MAX_INT 开始,所以结束就在那里。同样使用 i+= 1000000000 将使其不再是无限循环。试试看。
  • @TedBigham:哎呀,准备得太快了。谢谢你纠正我两次。
  • 请参阅吉米关于 9,007,199,254,740,991 而不是 9,007,199,254,740,992 here 的论点。结合我的后续行动,这似乎很有说服力。
【解决方案4】:

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

例子:

(Infinity>100)
=> true

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

Math.pow(2,1024) === Infinity
=> true

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

【讨论】:

  • 有些东西告诉我无穷大不符合整数的条件。 :)
  • 但是当您寻找最小值时,初始化一个 min 变量就足够了。
  • 请注意Infinity - 1 === Infinity
  • 还有 (Infinity<100) => false 和 Math.pow(2,1024) === Infinity
  • 同样值得一提的是它也处理负无穷大。所以1 - Infinity === -Infinity
【解决方案5】:

Jimmy's answer 正确地将连续的 JavaScript 整数谱表示为-9007199254740992至9007199254740992包容(对不起9007199254740993,你可能认为你是9007199254740993,但你错了!下面或jsfiddle中的演示).

console.log(9007199254740993);

但是,没有以编程方式找到/证明这一点的答案(his answer 中提到的 CoolAJ86 将在 28.56 年内完成;),所以这里有一种稍微更有效的方法(准确地说,它更效率大约 28.559999999968312 年 :),以及 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 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);

【讨论】:

  • @CoolAJ86:大声笑,我很期待 2040 年 3 月 15 日。如果我们的数字匹配,我们应该举办派对 :)
  • var x=Math.pow(2,53)-3;while (x!=x+1) x++; -> 9007199254740991
  • @MickLH:我得到 9007199254740992 that code。您使用什么 JavaScript 引擎进行测试?
  • 你用自己的代码得到 9007199254740992,我没有使用 x 的最终值,而是出于偏执的原因对 x++ 进行最终评估。谷歌浏览器顺便说一句。
  • @MickLH:评估x++ 给你 x 的值前增量已经发生,所以这可能解释了差异。如果希望表达式的计算结果与 x 的最终值相同,则应将其更改为 ++x。
【解决方案6】:

许多早期的答案表明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

我们可以看到,在大于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, radix
                                       point has no way to go, but
                                       there is still one 2 left in
                                       exponent part)
  =>  1 . 0000 ---- 0000  *  2^53 
         |-- 52 bits --| 

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

                            (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: 18014398509481985
input: 18014398509481984 + 2  output: 18014398509481984  // expected: 18014398509481986
input: 18014398509481984 + 3  output: 18014398509481984  // expected: 18014398509481987
input: 18014398509481984 + 4  output: 18014398509481988  // expected: 18014398509481988

[之间的数字怎么样?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  --|

二进制中的值 0.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  
            

少于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
/**
   Please note that if you try this yourself and, say, log 
   these numbers to the console, they will get rounded. JavaScript
   rounds if the number of digits exceed 17. The value 
   is 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 位。

来自Wikipedia(更多详情,去那里)

所以要使指数部分为 2^52,我们确实需要设置 e = 1075。

【讨论】:

    【解决方案7】:

    为了安全

    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);
    }());
    

    【讨论】:

    • 你不能从 2^53 - 2 开始测试吗? (是的,你可以,我只是试过了,即使 -3 是安全的:var x=Math.pow(2,53)-3;while (x!=x+1) x++;) -> 9007199254740991
    • 好答案!此外,我知道该值已确定,但为什么不使用二进制搜索来查找它呢?
    • 那有什么乐趣呢?此外,@Briguy37 打败了我:stackoverflow.com/a/11639621/151312
    • 请注意,在与 Date 值进行比较时,这个基于 32 位的“安全”MAX_INT 将不起作用。 4294967295 昨天就是这样!
    • 答案“安全起见:var MAX_INT = 4294967295;”不幽默。如果您不进行位移,请不要担心(除非您需要一个大于 4294967295 的整数,在这种情况下,您可能应该将其存储为字符串并使用 bigint 库)。
    【解决方案8】:

    简短的回答是“视情况而定”。

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

    未签名:0…(-1&gt;&gt;&gt;0)

    签名:(-(-1&gt;&gt;&gt;1)-1)…(-1&gt;&gt;&gt;1)

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

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

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

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

    【讨论】:

    • 这是我想偶然发现的关于如何将 X 转换为 32 位整数或无符号整数的答案。赞成你的回答。
    【解决方案9】:

    ECMA 脚本 6:

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

    【讨论】:

    • 谨防this is not (yet) supported by all browsers! 今天 iOS(甚至不是 chrome)、Safari 和 IE 都不喜欢它。
    • 请仔细阅读答案,我们没有使用 ECMAScript 6 中 Number.MAX_SAFE_INTEGER 的默认实现,我们通过 Math.pow(2, 53)-1 定义它
    • 我认为这只是对它在 ECMA 6 中如何实现的参考! :P 不过,我认为我的评论仍然有效。都是上下文问题。 ;)
    • 通过向后工作在所有浏览器中计算MAX_SAFE_INTEGER是否可靠?你应该继续前进吗?即 Number.MAX_SAFE_INTEGER = 2 * (Math.pow(2, 52) - 1) + 1;
    • Math.pow(2, 53)-1 安全操作吗?它比最大的安全整数大一个。
    【解决方案10】:

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

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

    这让我在 Chrome 30 中不到一毫秒就得到了 9007199254740992。

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

    【讨论】:

    • 它可能会使您的应用程序崩溃,我想。
    【解决方案11】:

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

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

    【讨论】:

      【解决方案12】:

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

      var a = 123456789012345678901012345678901n;
      

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

      而且,很明显,由于有限的内存,总会有精度限制,以及分配必要的内存和对如此大的数字执行算术的时间成本。

      例如,生成十万小数位的数字,在完成之前会有明显的延迟:

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

      ...但它有效。

      【讨论】:

        【解决方案13】:

        尝试:

        maxInt = -1 >>> 1
        

        在 Firefox 3.6 中是 2^31 - 1。

        【讨论】:

        • @danorton:我不确定你明白你在做什么。 ^意味着上台.在 javascript 控制台中,^ 是异或, 未升至
        • 打开 Chrome/Firefox 控制台。键入 5^2。在二进制中,5 是101,2 是010。现在,如果您对它们进行按位异或,您将得到 5(101) ^ 2(010) = 7(111) READ THIS IF YOU'RE CONFUSED 这里讨论的是 Math.pow() 而不是 ^ 运算符
        • 再一次,我一点也不困惑。我对什么是发表了评论并投了反对票书面.如果 Math.pow() 是什么意思,那么这就是应该写的。在回答有关 JavaScript 的问题时,使用不同语言的语法是不合适的。使用在 JavaScript 中有效的语法,但在 JavaScript 中的解释与预期的含义不同,这是更不合适的。
        • 2^31 是英文中2的31次方的写法。它不在代码块中。你会抱怨有人使用 ;在答案中,因为这是一个在 Javascript 中具有不同含义的字符?
        • 尽管一个应该写2³¹并不是2^31在纯文本中,这样做很常见,因为大多数键盘布局默认情况下没有这些字符。至少我在理解这个答案的意思时没有任何问题。
        【解决方案14】:

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

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

        【讨论】:

        • 请注意 9e15 = 2^53(请参阅@Jimmy 的回答)。
        • 9e15 = 9000000000000000。2^53 = 9007199254740992。因此,迂腐地说,9e15 仅约等于 2^53(有两位有效数字)。
        • @chaiguy 在9000000000000000 中有 1 个有效数字。在`9007199254740992`中有15位有效数字。
        • @RoyiNamir 不想在这里开始毫无意义的争论,但是 9000000000000000 有 16 位有效数字。如果您只想要 1,则必须写成 9x10^15。
        • @chaiguy 号 9000000000000000 原样 - 有 1 SF。其中 90*10^14 有 2. (sigfigscalculator.appspot.com) & mathsfirst.massey.ac.nz/Algebra/Decimals/SigFig.htm (底部)
        【解决方案15】:

        我这样写:

        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;
        

        【讨论】:

          【解决方案16】:

          让我们去sources

          描述

          MAX_SAFE_INTEGER 常量的值为 9007199254740991(9,007,199,254,740,991 或 ~9 千万亿)。该数字背后的原因是 JavaScript 使用 IEEE 754 中指定的 double-precision floating-point format numbers,并且只能安全地表示 -(2^53 - 1) 和 2^53 - 1 之间的数字。

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

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

          浏览器兼容性

          【讨论】:

            【解决方案17】:

            【讨论】:

            • 这是很重要的一点。这就是为什么我在这里谷歌搜索最大 int 大小。其他答案建议 53 位,所以我将其编码为认为我可以安全地对高达 52 位的正值进行按位算术。但是它在 31 位之后失败了。谢谢@Marwen
            【解决方案18】:

            在 Google Chrome 内置的 javascript 中,可以到大约 2^1024 之前的数字称为无穷大。

            【讨论】:

            【解决方案19】:

            斯卡托写道:

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

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

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

            【讨论】:

              【解决方案20】:

              Firefox 3 似乎没有大量的问题。

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

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

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

              【讨论】:

              • 它不是 64 位整数,它是 64 位浮点数,其中 52/53 位是整数部分。所以它最多可以处理 1e300,但精度不高。
              • 吉米是对的。在您的浏览器或 JS 命令行中尝试此操作:100000000000000010 - 1 =&gt; 100000000000000020
              【解决方案21】:

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

              Number.MAX_VALUE = 1.7976931348623157e+308
              

              【讨论】:

              • -1:最大可表示(非精确整数)数可能是 ~2^1024,但这并不意味着它们偏离了 IEEE-75464位标准。
              • MAX_INT?你是说 MAX_VALUE?
              • 那是最大的浮点价值。这并不意味着您可以存储那么长的整数
              • 或者更重要的是,你不能可靠地存储那么长的整数不损失准确性. 2^53 被称为 MAX_SAFE_INT,因为在该点之上,值变成近似值,就像分数一样。
              猜你喜欢
              • 2022-03-02
              • 2010-09-23
              • 2022-03-02
              相关资源
              最近更新 更多