I know (-0 === 0) comes out to be true. I am curious to know why -0 < 0 happens?

When I run this code in stackoverflow execution context, it returns `0`

.

```
const arr = [+0, 0, -0];
console.log(Math.min(...arr));
```

But when I run the same code in the browser console, it returns `-0`

. Why is that? I have tried to search it on google but didn't find anything useful. This question might not add value to someone practical example, I wanted to understand how does JS calculates it.

```
const arr = [+0, 0, -0];
console.log(Math.min(...arr)); // -0
```

ยท
Santiago Trujillo

This is a specialty of `Math.min`

, as specified:

21.3.2.25 Math.min ( ...args )[...]

- For each element number of coerced, do
a. If number is NaN, return NaN.

b. If number is -0๐ฝ and lowest is +0๐ฝ, set lowest to -0๐ฝ.c. If number < lowest, set lowest to number.

- Return lowest.

Note that in most cases, +0 and -0 are treated equally, also in the ToString conversion, thus `(-0).toString()`

evaluates to `"0"`

. That you can observe the difference in the browser console is an implementation detail of the browser.

ยท
Santiago Trujillo
Report

The spec is curiously contradictory. The `<`

comparison rule explicitly says that `-0`

is not less than `+0`

. However, the spec for `Math.min()`

says the opposite: if the current (while iterating through the arguments) value is `-0`

, and the smallest value so far is `+0`

, then the smallest value should be set to `-0`

.

I would like for somebody to activate the T.J. Crowder signal for this one.

*edit* โ it was suggested in some comments that a possible reason for the behavior is to make it possible to detect a `-0`

value, even though for almost all purposes in normal expressions the `-0`

is treated as being plain `0`

.

ยท
Santiago Trujillo
Report

`-0`

is not less than `0`

or `+0`

, both `-0 < 0`

and `-0 < +0`

returns `False`

, you're mixing the behavior of `Math.min`

with the comparison of `-0`

with `0`

/`+0`

.

The specification of `Math.min`

is clear on this point:

b. If number is -0๐ฝ and lowest is +0๐ฝ, set lowest to -0๐ฝ.

Without this exception, the behavior of `Math.min`

and `Math.max`

would depend on the order of arguments, which can be considered an odd behavior โ you probably want `Math.min(x, y)`

to always equal `Math.min(y, x)`

โ so that might be one possible justification.

**Note:** This exception was already present in the 1997 specification for `Math.min(x, y)`

, so that's not something that was added later on.

ยท
Santiago Trujillo
Report

The point of this answer is to explain why the language design choice of having `Math.min`

be fully commutative makes sense.

I am curious to know why -0 < 0 happens?

It doesn't really; `<`

is a separate operation from "minimum", and `Math.min`

isn't based solely on IEEE `<`

comparison like `b<a ? b : a`

.

That would be non-commutative wrt. NaN as well as signed-zero. (`<`

is false if either operand is NaN, so that would produce `a`

).

As far as principle of least surprise, it would be at least as surprising (if not moreso) if `Math.min(-1,NaN)`

was `NaN`

but `Math.min(NaN, -1)`

was `-1`

.

The JS language designers wanted `Math.min`

to be NaN-propagating, so basing it just on `<`

wasn't possible anyway. **They chose to make it fully commutative including for signed zero, which seems like a sensible decision.**

OTOH, most code doesn't care about signed zero, so this language design choice costs a bit of performance for everyone to cater to the rare cases where someone wants well-defined signed-zero semantics.

If you want a simple operation that ignores NaN in an array, iterate yourself with `current_min = x < current_min ? x : current_min`

. That will ignore all NaN, and also ignore `-0`

for `current_min <= +0.0`

(IEEE comparison). Or if `current_min`

starts out NaN, it will stay NaN. Many of those things are undesirable for a `Math.min`

function, so it doesn't work that way.

**If you compare other languages**, the C standard `fmin`

function is commutative wrt. NaN (returning the non-NaN if there is one, opposite of JS), but is not required to be commutative wrt. signed zero. Some C implementations choose to work like JS for +-0.0 for `fmin`

/ `fmax`

.

But C++ `std::min`

*is* defined purely in terms of a `<`

operation, so it *does* work that way. (It's intended to work generically, including on non-numeric types like strings; unlike `std::fmin`

it doesn't have any FP-specific rules.) See What is the instruction that gives branchless FP min and max on x86? re: x86's `minps`

instruction and C++ `std::min`

which are both non-commutative wrt. NaN and signed zero.

IEEE 754 `<`

doesn't give you a total order over distinct FP numbers. `Math.min`

does except for NaNs (e.g. if you built a sorting network with it and `Math.max`

.) Its order disagrees with `Math.max`

: they both return NaN if there is one, so a sorting network using min/max comparators would produce all NaNs if there were any in the input array.

`Math.min`

alone wouldn't be sufficient for sorting without something like `==`

to see which arg it returned, but that breaks down for signed zero as well as NaN.

ยท
Santiago Trujillo
Report