【问题标题】:Why does Math.min() return positive infinity, while Math.max() returns negative infinity?为什么 Math.min() 返回正无穷大,而 Math.max() 返回负无穷大?
【发布时间】:2012-02-09 13:13:01
【问题描述】:

当我在javascript数学最小和最大函数的参数中输入一个数组时,它会返回正确的值:

console.log( Math.min( 5 ) ); // 5
console.log( Math.max( 2 ) ); // 2

var array = [3, 6, 1, 5, 0, -2, 3];
var minArray = Math.min( array ); // -2
var maxArray = Math.max( array ); // 6

但是,当我使用不带参数的函数时,它会返回错误的答案:

console.log( Math.min() ); // Infinity
console.log( Math.max() ); // -Infinity

这个返回false:

console.log( Math.min() < Math.max() );

为什么要这样做?

【问题讨论】:

  • 你用的是什么浏览器? Math.min/max 不适用于数组。
  • Chrome 版本:16.0.912.75 m
  • 我也是,你确定你有-26吗?他们应该是NaN
  • 为什么不带任何参数调用Math.MinMath.Max
  • 看看wtfjs.com,第一项。

标签: javascript math max min


【解决方案1】:

当然会,因为对于Math.min,起始编号应该是Infinity。所有小于正无穷大的数字都应该是列表中最小的,如果没有更小的。

对于Math.max 也是一样;如果没有更大的数字,所有大于负无穷大的数字都应该是最大的。

所以对于你的第一个例子:

Math.min(5) 其中5 小于正无穷大 (Infinity),它将返回 5

更新

使用数组参数调用Math.min()Math.max 可能不适用于每个平台。您应该改为执行以下操作:

Math.min.apply(null, [ 1, 2, 3, 4 , 5 ]);

第一个参数是作用域参数。因为Math.min()Math.max() 是“静态”函数,我们应该将范围参数设置为null。

【讨论】:

  • 这是聪明但不体贴的设计师认为理论比可用性更重要的完美例子。我理解你为什么建议这个“起始数字”的概念是理智的甚至是正确的,但在我看来,这是算法/实现泄漏到结果中。对于任何接近该语言的人来说,这绝不是显而易见的——在将Math.min() 作为一行代码阅读时,我希望它返回的最后一件事是 Infinity(可能的最大数字)。我很欣赏 JS,但我会把它直接归入“遗憾的决定”类别。
  • @JacobSwartwood 并且因为您不同意您的设计downvote 我的回答。不错!
  • 我没有投反对票,因为我不同意这个设计,我实际上赞成下面的答案,它说了同样的话,但更详细。你投了我的反对票纯粹是因为“当然会……”。
【解决方案2】:

正确决定聚合函数在传递空集时应该做什么虽然很棘手,但很重要。

有时它“直观地明显”:没有元素的总和是多少?零,我相信每个人都会很高兴地说。

有时情况并非如此:没有元素的产品是什么?受过一些数学训练的人会很快说“一”,但这一点也不明显。

然后你得到了 MIN 和 MAX,哇!我们是如何得到这些无穷大的?


决定聚合函数在这里应该做什么的一种方法是考虑我们想要保持一致的行为,即使是空集。例如,假设我们有这些非空集:

A = { 1, 2, 3 } 
B = { 4, 5 }

现在,这里是真的,而且确实对于任何非空集合,

SUM(A ∪ B) = SUM({SUM(A), SUM(B)})
15 = 6 + 9

PRODUCT(A ∪ B) = PRODUCT({ PRODUCT(A), PRODUCT(B) })
120 = 6 * 20

MIN(A ∪ B) = MIN({ MIN(A), MIN(B) })
1 = MIN(1, 4)

数学家们说,如果这些性质在其中一个或两个集合为空的情况下仍然成立,那不是很好吗?肯定会的。

并且它维护 行为决定我们分配给 SOME_AGGREGATE_FUNCTION(∅) 的值:

为了

SUM(A ∪ B) = SUM({ SUM(A), SUM(B) })

要在A 为空而B 不为空时保持为真,我们必须有SUM(∅) = 0

为了

PRODUCT(A ∪ B) = PRODUCT({ PRODUCT(A), PRODUCT(B) })

A 为空而B 不为空时保持真实,我们必须有PRODUCT(∅) = 1

最后:

为了

MIN(A ∪ B) = MIN({ MIN(A), MIN(B) })

A 为空而B 不为空时保持为真,我们需要MIN(∅) 是一个保证大于B 中任何可能值的值,这样它就不会“干扰” ' MIN(B) 的结果。我们得到了答案:MIN(∅) = +∞

【讨论】:

    【解决方案3】:

    为什么要这样做?

    因为这就是the standard says 应该发生的事情;

    15.8.2.11 最大值 ([ value1 [ , value2 [ , … ] ] ])

    给定零个或多个参数,对每个参数调用 ToNumber 并返回结果值中的最大值。

    • 如果没有给出参数,则结果为 -Infinity
    • 如果任何值为 NaN,则结果为 NaN。
    • 比较值以确定最大值的方法与 11.8.5 相同,但 +0 被认为大于 -0。

    15.8.2.12 分钟 ([ value1 [ , value2 [ , … ] ] ])

    给定零个或多个参数,对每个参数调用 ToNumber 并返回结果值中的最小值。

    • 如果没有给出参数,则结果为无穷大。
    • 如果任何值为 NaN,则结果为 NaN。
    • 比较值以确定最小值的方法与 11.8.5 相同,但 +0 被认为大于 -0。

    附言; Math.max()Math.min() 接受数组是非标准的。请改用Math.max(a,b,c,d,e,...) 等。

    至少在 Chrome 中;

    Math.max([1,2,3,4]); // NaN
    

    【讨论】:

    • 该死的,打败我。主要是因为我花了更多时间格式化;P
    • 这是为空集定义最小值和最大值的“逻辑”方式,但可能不是常识 ;-)
    【解决方案4】:

    空列表的和通常定义为 0,它们的积通常定义为 1 的原因相同:它是操作的标识元素。也就是说,只要在计算最大值时包含 -Infinity 作为元素,它不会影响结果;分别为 Infinity 和 min.

    这是明智的,因为它允许聚合操作具有所需的“关联”属性。例如,列表的总和与计算任何子列表(可能包括空列表)的总和并将它们相加是相同的。对于产品、最小值、最大值等也是如此。

    【讨论】:

      【解决方案5】:

      [ECMA-262: 15.8.2.11]:max ( [ value1 [ , value2 [ , ... ] ] ] )

      给定零个或多个参数,对每个参数调用 ToNumber 并返回结果值中的最大值。

      • 如果没有给出参数,则结果为 -∞。
      • 如果任何值为NaN,则结果为NaN
      • 比较值以确定最大值的方法与 11.8.5 相同,但 +0 被认为大于 -0。

      max 方法的 length 属性为 2。

      [ECMA-262: 15.8.2.12]:min ( [ value1 [ , value2 [ , ... ] ] ] )

      给定零个或多个参数,对每个参数调用 ToNumber 并返回结果值中的最小值。

      • 如果没有给出参数,则结果为 +∞。
      • 如果任何值为NaN,则结果为NaN
      • 比较值以确定最小值的方法与 11.8.5 相同,但 +0 被认为大于 -0。

      min 方法的length 属性为 2。

      没有参数,Math.min 是一个可用于计算迭代最小值的值,而不是该类型的物理最小值。它通过相反的方式做到这一点:类型的物理最大值。 (Math.max 的另一个方向类似;并且显然+∞ &lt; -∞false

      var a = [1,2,3,4,5];
      var x = Math.min();
      for (var i = 0; i < a.length; i++) {
          if (a[i] < x) { // will succeed on first iteration
                          // because `x` is initialised to highest possible value
             x = a[i];
          }
      }
      

      (事实上,可能只是标准使Math.min 的实现更容易,因为它可能在处理任何存在的参数之前将其结果初始化为 +Infinity ,使用类似于上述的算法。)

      当然,这个例子有点做作,因为我们可以写:

       var x = Math.min(a[0], a[1], a[2], a[3], a[4]);
      

      但是,如果我们不知道数组中元素的数量,则循环很有用,因为您使用的接受数组的 Math.min 的变体是非标准的。

      即使这样,你也可以这样做:

       var x = Math.min.apply(null, a);
       //               ^ reflective function-call
       //                     ^ no object instance; function is "static"
       //                           ^ array of arguments
      

      【讨论】:

        【解决方案6】:

        可能是因为在开始与空数组进行比较之前,实现将内部比较变量初始化为最高(对于 Math.min)或最低(对于 Math.max),然后返回此内部比较变量的值当然没有改变。

        【讨论】:

        • 哦,我明白了。不过,这不仅仅是关于实现:这个结果是由标准定义的。
        【解决方案7】:

        我不确定。但是,只是猜测。

        记住我们是如何找到最小值的。声明一个具有极高值(Infinity)的变量,然后遍历这些值,每当您发现一个小于存储在变量中的值时,将其存储为新的最小值。

        因此,由于您没有给它任何值来找到最小值,它给您初始值,即无穷大。

        最大值相同。

        【讨论】:

          【解决方案8】:

          当不给定参数时,Math.min() 等于无穷大,Math.max() 等于 -infinity

          这可能是为了确保任何值都小于迄今为止找到的最小值,并且大于迄今为止的最大值。

          【讨论】:

          • 我认为他的问题是为什么会这样?不应该是相反的吗?
          【解决方案9】:

          这个想法是,在数学上没有任何参数你实际上有一个未定义的最小值。

          在实现方面,通常最小值用一个非常大的值(无穷大)初始化,然后在找到更小的值时更新。如果没有找到值,那么您将Infinity 作为最小值。

          情况与找到最大值相反,这就是你有 -Infinity 的原因。

          【讨论】:

            【解决方案10】:

            理论上无法给出这些函数的结果。 Ecma specification 指示不带参数的 Min 和 Max 函数的结果(参见第 163 页)。

            显然,对于结果应该是什么,您可以有各种各样的争论,但无论如何都没有严格正确的答案。我猜 Ecma 选择这个是因为它最容易实现。通常一个 max 函数大致是这样工作的

            result = -infinity;
            foreach(arg in arguments)
                if(arg > result)
                    result = arg;
            

            如您所见,在不带参数的情况下调用函数时返回 -infinity 根本不需要任何更改。

            【讨论】:

              猜你喜欢
              • 2022-01-23
              • 2016-11-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多