Sé que (-0 === 0) resulta ser cierto. Tengo curiosidad por saber por qué sucede -0 <0.
Cuando ejecuto este código en el contexto de ejecución de stackoverflow, devuelve 0
.
const arr = [+0, 0, -0]; console.log(Math.min(...arr));
Pero cuando ejecuto el mismo código en la consola del navegador, devuelve -0
. ¿Porqué es eso? He intentado buscarlo en google pero no he encontrado nada útil. Es posible que esta pregunta no agregue valor a un ejemplo práctico, quería entender cómo lo calcula JS.
const arr = [+0, 0, -0]; console.log(Math.min(...arr)); // -0
Esta es una especialidad de Math.min
, como se especifica :
21.3.2.25 Math.min (... argumentos)
[...]
- Para cada número de elemento de coaccionado, haga
una. Si el número es NaN, devuelve NaN.
B. Si el número es -0𝔽 y el más bajo es +0𝔽, establezca el más bajo en -0𝔽.
C. Si el número < más bajo, establezca el número más bajo.
- Retorno más bajo.
Tenga en cuenta que, en la mayoría de los casos, +0 y -0 se tratan por igual, también en la conversión ToString, por lo que (-0).toString()
se evalúa como "0"
. Que puedas observar la diferencia en la consola del navegador es un detalle de implementación del navegador.
La especificación es curiosamente contradictoria. La regla de comparación <
dice explícitamente que -0
no es menor que +0
. Sin embargo, la especificación para Math.min()
dice lo contrario: si el valor actual (mientras se itera a través de los argumentos) es -0
, y el valor más pequeño hasta ahora es +0
, entonces el valor más pequeño debe establecerse en -0
.
Me gustaría que alguien activara la señal de TJ Crowder para este.
editar : se sugirió en algunos comentarios que una posible razón para el comportamiento es permitir detectar un valor -0
, aunque para casi todos los propósitos en expresiones normales, el -0
se trata como si fuera un 0
simple.
-0
no es menor que 0
o +0
, ambos -0 < 0
y -0 < +0
devuelven False
, está mezclando el comportamiento de Math.min
con la comparación de -0
con 0
/ +0
.
La especificación de Math.min
es clara en este punto:
B. Si el número es -0𝔽 y el más bajo es +0𝔽, establezca el más bajo en -0𝔽.
Sin esta excepción, el comportamiento de Math.min
y Math.max
dependería del orden de los argumentos, lo que puede considerarse un comportamiento extraño: probablemente desee que Math.min(x, y)
sea siempre igual a Math.min(y, x)
, por lo que esa podría ser una posible justificación.
Nota: esta excepción ya estaba presente en la especificación de 1997 para Math.min(x, y)
, por lo que no es algo que se agregó más adelante.
El objetivo de esta respuesta es explicar por qué tiene sentido la elección del diseño del lenguaje de hacer que Math.min
sea completamente conmutativo.
Tengo curiosidad por saber por qué sucede -0 <0.
Realmente no; <
es una operación separada de "mínimo", y Math.min
no se basa únicamente en la comparación IEEE <
como b<a ? b : a
.
Eso sería wrt no conmutativo. NaN, así como cero con signo. ( <
es falso si alguno de los operandos es NaN, por lo que produciría a
).
En cuanto al principio de menor sorpresa, sería al menos tan sorprendente (si no más) si Math.min(-1,NaN)
fuera NaN
pero Math.min(NaN, -1)
fuera -1
.
Los diseñadores del lenguaje JS querían que Math.min
se propagara por NaN, por lo que basarlo solo en <
no era posible de todos modos. Eligieron hacerlo completamente conmutativo, incluido el cero con signo, lo que parece una decisión sensata.
OTOH, a la mayoría del código no le importa el cero con signo, por lo que esta elección de diseño de lenguaje cuesta un poco de rendimiento para que todos atiendan los casos raros en los que alguien quiere una semántica de cero con signo bien definida.
Si desea una operación simple que ignore NaN en una matriz, itérelo usted mismo con current_min = x < current_min ? x : current_min
. Eso ignorará todos los NaN y también ignorará -0
para current_min <= +0.0
(comparación IEEE). O si current_min
comienza en NaN, seguirá siendo NaN. Muchas de esas cosas no son deseables para una función Math.min
, por lo que no funciona de esa manera.
Si compara otros idiomas , la función fmin
estándar de C es wrt conmutativa. NaN (devolviendo el no-NaN si hay uno, opuesto a JS), pero no se requiere que sea conmutativo. cero firmado. Algunas implementaciones de C optan por funcionar como JS para +-0.0 para fmin
/ fmax
.
Pero C++ std::min
se define puramente en términos de una operación <
, por lo que funciona de esa manera. (Está diseñado para funcionar de forma genérica, incluso en tipos no numéricos como cadenas; a diferencia de std::fmin
, no tiene reglas específicas de FP). Consulte ¿Cuál es la instrucción que proporciona un mínimo y un máximo de FP sin sucursales en x86? re: la instrucción minps
de x86 y C++ std::min
, que son wrt no conmutativos. NaN y cero con signo.
IEEE 754 <
no le da un orden total sobre distintos números de FP. Math.min
excepto NaN (por ejemplo, si creó una red de clasificación con él y Math.max
). Su orden no está de acuerdo con Math.max
: ambos devuelven NaN si hay uno, por lo que una red de clasificación que usa comparadores min/max sería producir todos los NaN si hubiera alguno en la matriz de entrada.
Math.min
por sí solo no sería suficiente para ordenar sin algo como ==
para ver qué argumento devolvió, pero eso se descompone tanto para el cero con signo como para NaN.