• Jobs
  • About Us
  • professionals
    • Home
    • Jobs
    • Courses and challenges
  • business
    • Home
    • Post vacancy
    • Our process
    • Pricing
    • Assessments
    • Payroll
    • Blog
    • Sales
    • Salary Calculator

0

100
Views
Why does Math.min() return -0 from [+0, 0, -0]

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
over 3 years ago ยท Santiago Trujillo
4 answers
Answer question

0

This is a specialty of Math.min, as specified:

21.3.2.25 Math.min ( ...args )

[...]

  1. 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.

  1. 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.

over 3 years ago ยท Santiago Trujillo Report

0

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.

over 3 years ago ยท Santiago Trujillo Report

0

-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.

over 3 years ago ยท Santiago Trujillo Report

0

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.

over 3 years ago ยท Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Our process Sales
Legal
Terms and conditions Privacy policy
ยฉ 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recommend me some offers
I have an error