# Why is Math.max() less than Math.min()?

A while back I wrote a post called Javascript: the Weird parts primarily for my own amusement. It listed some of my personal favourite wtfjs-type Javascript idiosyncracies.

In this post I want to look in more depth at one of those idiosyncracies. I want to investigate exactly why Math.max() is less than Math.min(). Here’s what I mean, this is nodejs, but as we will discover, other implementations ought to behave the same.

```> Math.max()>Math.min() false > Math.max()<Math.min() true```

## What should Math.max() do?

Let’s start out by clarifying that Math.max() is not the same thing as Number.MAX_VALUE. It doesn’t return the largest possible number or anything like that.

In fact it is for comparing a list of numbers and returning the largest. Here’s the definition from the ECMA-262 standard.

15.8.2.11 max ( [ value1 [ , value2 [ , â€¦ ] ] ] )

Given zero or more arguments, calls ToNumber on each of the arguments and returns the largest of the resulting values.

If no arguments are given, the result is âˆ’âˆž.
If any value is NaN, the result is NaN.
The comparison of values to determine the largest value is done as in 11.8.5 except that +0 is considered to be larger than âˆ’0.

Let’s quickly check that is what happens in the real world.

```> Math.max(1,2,3) 3 > Math.max(3,2,1) 3 > Math.max(3) 3 > Math.max() -Infinity```

Math.min is defined similarly, except that it finds the smallest number in its argument list. And if given an empty list it returns positive infinity. Let’s try it.

```> Math.min(9) 9 > Math.min(9,1,7,2) 1 > Math.min() Infinity```

Both functions work. But why is the largest number in an empty list negative infinity?

## Folding with reduce

A nice way to look at this is through the lens of functional programming and abstract algebra. No, really. Bear with me.

Imagine that you wanted to implement max in a functional style. You want to reduce a list to the largest element in it. In the functional world, when we have a list that needs to be reduced to a single value it is typical to use a fold function — AKA reduce in Javascript and some other languages.

Reduce is an higher-order function. You pass it a function that works on two arguments, a function and a special initial value — the sense in which I mean special will become evident shortly. It applies the function to each element of the array, eventually reducing the contents of the entire array into a single value.

Say I have a list and I want to add up all the numbers in it. `list = [1,2,3]`

I can write a function that adds two numbers given as arguments, and then reduce the list to a single value using this function.

Here is my function. I’ll call it sum.`sum = function(special,n) { return special + n } `And I can call reduce with it and the special value 0 on a list and have it do as expected. ```> sum = function(special,n) { return special + n } [Function] > special = 0 0 > list.reduce(sum, special) 6```

For each element of the list we call sum with the special value, which is initialized to zero, and behaves like a running total.

Note how this is pretty much the same as taking a list and multiplying all the numbers in it. This time I will assign 1 to the *special* value. ```> product = function(special,n) { return special * n } [Function] > special = 1 1 > list.reduce(product, special) 6 ```

## Monoids, binary operations and identity elements, oh my!

Wikipedia defines a monoid as:

an algebraic structure with a single associative binary operation and an identity element.

Product and sum are binary operations. And the operations sure look associative, by which I mean sum(2,sum(3,4)) = sum(sum(2,3),4) and product(2,product(3,4)) = product(product(2,3),4). [ 1 ]

All we need to make a monoid is an identity element. So what is an identity element?

It is an element that, when you apply the binary operation to it and any other element, the binary operation will always return the other element.

The special value is our identity element.

0 is the identity of addition; 1 the identity of multiplication. By which I mean x + 0 is always equal to x. And x * 1 is always equal to x, too. Or alternatively sum(3,0) == 3 and product(3,1) == 3.

## Negative infinity

Astute readers will by now have realized that max is also monoid-like. And the really smart cookies will have spotted that its identity element is -Infinity.

If you think about it, max(-Infinity, x) is guaranteed to be x. Just as sum(0, x) is guaranteed to be x and product (1, x) to be x.

Implementing max goes like this. `max = function(identity, num) { return num > identity ? num : identity }`

And now we can find the largest number in the list. ```> list.reduce(max,-Infinity) 3```

If we used any other number than -Infinity as our identity, we would run in to problems. If it were Infinity, then max would always return Infinity. If it were 0 then, if our list of numbers had only negative numbers in it, max would return 0. [ 2 ]

For the sake of completeness, we can define min similarly.```> min = function(identity, num) { return num < identity ? num : identity } [Function] > list.reduce(min,Infinity) 1```

## In the real world

In the real world we might also choose to implement max with a for loop.

```function max(xs) { biggestSoFar = -Infinity for(i=0; i < xs.length; i++) { biggestSoFar = xs[i] > biggestSoFar ? xs[i] : biggestSoFar; } return biggestSoFar; }```

Note that we still need biggestSoFar to be -Infinity for our max function to work as expected. The logic is the same, but I find the functional version clearer. It is certainly shorter!

This is not too far off how max is actually implemented. Here is Google’s V8 implementation.```function MathMax(arg1, arg2) { // length == 2 var length = %_ArgumentsLength(); if (length == 0) { return -1/0; // Compiler constant-folds this to -Infinity. } var r = arg1; if (!IS_NUMBER(r)) r = NonNumberToNumber(r); if (NUMBER_IS_NAN(r)) return r; for (var i = 1; i < length; i++) { var n = %_Arguments(i); if (!IS_NUMBER(n)) n = NonNumberToNumber(n); if (NUMBER_IS_NAN(n)) return n; // Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be // a Smi or heap number. if (n > r || (r === 0 && n === 0 && !%_IsSmi(r) && 1 / r < 0)) r = n; } return r; }```

Note 1: Thanks to Michael Chermside for pointing out that I had originally described commutativity, not the associativity in this paragraph. Back.

Note 2: Thanks to andrew_404 for suggesting better wording for this paragraph. My original wording was misleading — for lists which contained a number >= 0 max would behave as expected. Back.

• Be respectful. You may want to read the comment guidelines before posting.
• You can use Markdown syntax to format your comments. You can only use level 5 and 6 headings.
• You can add class="your language" to code blocks to help highlight.js highlight them correctly.

Privacy note: This form will forward your IP address, user agent and referrer to the Akismet, StopForumSpam and Botscout spam filtering services. I don’t log these details. Those services will. I do log everything you type into the form. Full privacy statement.

• #701

Great article! You might want to fix your definition of associative. It doesn't mean that sum(a,b) = sum(b,a); that would be "commutative". Associative means that sum(a,sum(b,c)) = sum(sum(a,b),c).

• #703

Hey Michael Doh! Thanks so much for letting me know. I've corrected the text :-)

• #705

Great article! I was unaware of the implementation details of max and min and really enjoyed learning about them.

There's also a minor tweak I'd consider making to the article. When discussing why max needs to have -Infinity as its identity you say, "If it were 0 then, if our list of numbers had a negative number in it, max would return 0."

This isn't quite right as it might return a number higher than 0 if it appears in the list. It should probably read something like "If it were 0 then, if our list of numbers had only negative numbers in it, max would return 0."

Thanks again for the great article!

• #706

Hi andrew_404

Nice one. Yes, the original wording was misleading, I've updated the text. Thanks for the improvement :-)

• #707

I will point out one small typo, if you don't mind. When you describe the`sum` function for the first time, it doesn't include the`return` statement. Have you been writing too much CoffeeScript? ;)