【问题标题】:Calling parseInt with a string which represents a value larger than Number.MAX_SAFE_INTEGER使用表示大于 Number.MAX_SAFE_INTEGER 的值的字符串调用 parseInt
【发布时间】:2019-10-16 06:20:16
【问题描述】:

我想问一下parseInt 在使用代表大于Number.MAX_SAFE_INTEGER 的整数值的字符串调用时的行为。

从技术上讲,我认为我应该期待与直接使用该值(即,作为数字而不是字符串)相同的结果。

例如,以下两个将产生相同的值(无论其准确与否):

  • const x = parseInt("0x100000000000000000000000000000000");
  • const x = 0x100000000000000000000000000000000;

不过,我确实理解,也许 JS 不能保证这一点。

所以我真正想知道的是,当使用表示大于Number.MAX_SAFE_INTEGER 的整数值的字符串调用时,我是否至少可以指望parseInt 返回一个不同于0 的值。

【问题讨论】:

  • 它与 MAX_SAFE_INTEGER 上的任何其他数字具有完全相同的问题,因此它可能有效,也可能无效。
  • @VLAZ:谢谢。所以那里可能有一个非常大的整数,当使用它时(作为本机 Number,或作为传递给 parseInt 的字符串),将“环绕”为零?
  • 它不会环绕,但会不精确。如果数字很大,您可以获得2 + 2 = 5 或其他奇怪的行为。最终,底层的二进制数字不足以表示数字。
  • @VLAZ:是的,但我们是否不能保证这些二进制数字中至少有一个不同于 0(关于我的底线问题)?另外,请注意,我不是在问不精确的问题。我已经知道这个问题(我认为从我在问题中明确提到MAX_SAFE_INTEGER这一事实可以清楚地看出)。
  • 您可以将浮点表示视为科学记数法,例如,1.2e24 - 您保留前面和最后一位数字(有效和指数)并且 太高 投入更多指数1.2e48 的数字将是一个更高的数字,但您仍然用相同的数字表示它。因此,如果您“太高”,您仍然会得到一个 close 的数字,但您将无法计算出实际的数字 - 1.001e3 将转换为1001,但如果你截断到小数点后一位 1.0e3 将被理解为 1000,你会丢失最后一个数字。

标签: javascript integer


【解决方案1】:

问题是数字表示的根本问题 - 任何 超过Number.MAX_SAFE_INTEGER 的数字不再安全使用。基本示例:

const max = Number.MAX_SAFE_INTEGER;
const maxPlus1 = Number.MAX_SAFE_INTEGER + 1;
const maxPlus2 = Number.MAX_SAFE_INTEGER + 2;

console.log("max:", max); //9007199254740991
console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992

console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true

如您所见,几乎在您突破Number.MAX_SAFE_INTEGER 障碍之后,您就会遇到精度问题。 JavaScript 使用 IEEE 754 standard 来表示数字,虽然它可以显示比其最高值的数字(与其他语言中的 int 字段相反,它将溢出到零或最大负数),这样的表示是不精确。一些大数字不能像9007199254740993(即Number.MAX_SAFE_INTEGER + 2)那样表示,而是得到一个不同的数字。

同样的事情也适用于parseInt,因为它将字符串转换为 JavaScript 数字,可能没有精确的表示:

const maxPlus1String = "9007199254740992";
const maxPlus2String = "9007199254740993";

const maxPlus1 = parseInt(maxPlus1String);
const maxPlus2 = parseInt(maxPlus2String);

console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992

console.log("(string) max + 1 = max + 2:", maxPlus1String === maxPlus2String); //false
console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true

最终,这是一个浮点数如何表示的问题。 Wikipedia has a good article 但我会将其简化为最重要的部分:

使用浮点表示,您可以保留 尾数(也称为 significand,末尾带有 d)和 exponent em> 为每个数字。这就像科学记数法一样工作,所以我将使用它以便于参考:

1.23e5 = 1.23 * 105 = 123 000

  • 1.23 是数字的尾数。
  • 5 是指数。

使用这两种方法,您可以用非常短的形式表示任意高的数字。但是,使用浮点表示,您可以保留每个位的位数。这是以牺牲准确性为代价的,一旦你用完尾数的数字,你就会失去准确性。因此,如果我们决定在科学记数法中只允许一位小数,我们会得到数字1.2e5,可能是 123 000,但也可能是 120 000 或 125 000 或 128 215 - 我们无法从缩短的形式重新创建它。浮点数也会发生类似的情况——一旦你没有足够的尾数位数,其余的就会被丢弃,所以你不会得到确切的数字。

当指数用完数字时,您会达到可表示的最高数字。

在 JavaScript 中,可能的最大数量可以在Number.MAX_VALUE 中看到:

console.log(Number.MAX_VALUE)

1.7976931348623157e+308相当大,指数为308。所以你可以用这个来表示很多数字,如果你在这个值下使用parseInt,你会得到在你解析的区域内的一些数字。

但是,如果您超过会发生什么?好吧,您将获得一个在 JavaScript 中可表示的数字范围内的值,该值是出于特殊原因而保留的。这是 绝对 最高数 - 一个浮点表示,表示可能表示的最大数。该值为Infinity。如果您碰巧解析了大于Number.MAX_VALUE 的内容,您将得到Infinity:

const largeNum = "17" + "0".repeat(307); //1.7e308
const tooLargeNum = "18" + "0".repeat(307); //1.8e308

console.log("large number string:", largeNum);
console.log("large number parsed:", parseInt(largeNum));


console.log("too large number string:", tooLargeNum);
console.log("too large number parsed:", parseInt(tooLargeNum));

因此,即使您有天文数字,您也可以保证有一个大于零的数字,因为Infinity > 0

【讨论】:

  • 谢谢。你的回答很好,但就像我说的,我的问题不是不精确,因为我很清楚这个问题。我想知道: 1. 我是否可以指望praseInt(str) 返回与直接使用str 作为本机数字所获得的完全相同的值。 2. 我是否至少可以指望它返回一个不同于 0 的值。
  • 那么你能保证对于每个表示 2^53 和(大约)10^309 之间的整数的字符串,praseInt 将返回一个非零值吗?
  • @goodvibration 是的,我可以。在 C++ 或 Java 中,没有像 int 字段那样的“环绕”——它们是有界整数。我已经(简要地)解释了浮点是如何工作的 - 如果您表示的数字高于允许您失去精度的精度,那么您不会得到完全不同的数字。精度损失意味着你得到一个具有 相似 值的数字,所以你不会得到数量级的不同。
猜你喜欢
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 2018-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多