Gostaria de saber se existem maneiras não triviais de encontrar o sinal de número ( função signum )?
Podem ser soluções mais curtas / rápidas / mais elegantes do que a óbvia
var sign = number > 0 ? 1 : number < 0 ? -1 : 0;
Resposta curta!
Use isso e você estará seguro e rápido (fonte: moz )
if (!Math.sign) Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; };
Você pode querer observar o desempenho e o violino de comparação de coerção de tipo
Muito tempo se passou. Além disso, é principalmente por razões históricas.
Resultados
Por enquanto, temos estas soluções:
1. Óbvio e rápido
function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }
1.1. Modificação de kbec - um tipo de elenco a menos, mais desempenho, mais curto [mais rápido]
function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
Cuidado: sign("0") -> 1
2. Elegante, curto, não tão rápido [mais lento]
function sign(x) { return x && x / Math.abs(x); }
CUIDADO: sign(+-Infinity) -> NaN
,sign("0") -> NaN
Como Infinity
é um número legal em JS, essa solução não parece totalmente correta.
3. A arte ... mas muito lenta [mais lenta]
function sign(x) { return (x > 0) - (x < 0); }
4. Usando bit-shift
rápido, massign(-Infinity) -> 0
function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }
5. Tipo seguro [megafast]
! Parece que os navegadores (especialmente o Chrome v8) fazem algumas otimizações mágicas e esta solução acaba tendo muito mais desempenho do que outras, até mesmo do que (1.1), apesar de conter 2 operações extras e logicamente nunca pode ser mais rápida.
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}
Ferramentas
- testes de desempenho jsperf ;
- violino - testes de tipo fundido;
Melhorias são bem-vindas!
[Offtopic] Resposta aceita
Andrey Tarantsov - +100 para a arte, mas infelizmente é cerca de 5 vezes mais lento do que a abordagem óbvia
Frédéric Hamidi - de alguma forma a resposta mais votada (por enquanto) e é bem legal, mas definitivamente não é como as coisas deveriam ser feitas, imho. Além disso, ele não lida corretamente com números do Infinity, que também são números, você sabe.
kbec - é uma melhoria da solução óbvia. Não tão revolucionário, mas no conjunto considero esta abordagem a melhor. Vote nele :)
fonte
0
é um caso especialtest everything
versão, o Safe se recusará a testar os valores especiais, então será mais rápido! Emonly integers
vez disso, tente executar o teste. Além disso, JSPerf está apenas fazendo seu trabalho, não é uma questão de gostar. :)typeof x === "number"
coloca um pouco de mágica na performance. Por favor, faça mais execuções, especialmente FF, Opera e IE para deixar isso claro.Math.sign()
(0 === 0, não tão rápido quanto "Seguro") que apareceu no FF25 e será lançado no Chrome.Respostas:
Versão mais elegante de solução rápida:
fonte
var sign = (number)? ((number < 0)? -1 : 1 ) : 0
Math.sign(number)
Dividir o número por seu valor absoluto também fornece seu sinal. Usar o operador AND lógico de curto-circuito nos permite criar casos especiais
0
para não acabarmos nos dividindo por ele:fonte
var sign = number && number / Math.abs(number);
no casonumber = 0
0
precisa ser especial. Resposta atualizada em conformidade. Obrigado :)A função que você está procurando é chamada signum , e a melhor maneira de implementá-la é:
fonte
Isso não deveria suportar zeros assinados de JavaScript (ECMAScript)? Parece funcionar ao retornar x em vez de 0 na função “megafast”:
Isso o torna compatível com um rascunho do Math.sign ( MDN ) do ECMAScript :
fonte
Para quem está interessado no que está acontecendo com os navegadores mais recentes, na versão ES6 existe um método nativo Math.sign . Você pode verificar o suporte aqui .
Basicamente ele retorna
-1
,1
,0
ouNaN
fonte
Super rápido se você não precisa do Infinity e sabe que o número é um inteiro, encontrado na fonte openjdk-7:
java.lang.Integer.signum()
fonte
Pensei em adicionar isto apenas por diversão:
0 e NaN retornará -1
funciona bem em +/- Infinity
fonte
Uma solução que funciona em todos os números, bem como
0
e-0
, bem comoInfinity
e-Infinity
, é:Consulte a pergunta " +0 e -0 são iguais? " Para obter mais informações.
Aviso: Nenhuma dessas respostas, incluindo o agora padrão
Math.sign
trabalho vontade sobre o caso0
vs-0
. Isso pode não ser um problema para você, mas em certas implementações de física pode ser importante.fonte
Você pode alterar o número de bits e verificar o bit mais significativo (MSB). Se o MSB for 1, o número é negativo. Se for 0, o número é positivo (ou 0).
fonte
var sign = number < 0 : 1 : 0
n & 0x80000000
como um bitmask. Quanto à conversão para 0,1, -1:n && (n & 0x80000000 ? -1 : 1)
-5e32
e quebrou.ToInt32
. Se você ler aqui (seção 9.5), há um módulo que afeta o valor dos números, uma vez que o intervalo de um inteiro de 32 bits é menor do que o intervalo do tipo número js. Portanto, não funcionará para esses valores ou para o infinito. Ainda gosto da resposta.Eu estava prestes a fazer a mesma pergunta, mas cheguei a uma solução antes de terminar de escrever, vi que essa pergunta já existia, mas não vi essa solução.
(n >> 31) + (n > 0)
parece ser mais rápido adicionando um embora ternário
(n >> 31) + (n>0?1:0)
fonte
Muito semelhante à resposta de Martijn é
Acho mais legível. Além disso (ou, dependendo do seu ponto de vista, no entanto), também groca coisas que podem ser interpretadas como um número; por exemplo, ele retorna
-1
quando apresentado com'-5'
.fonte
Não vejo nenhum sentido prático de retornar -0 e 0,
Math.sign
então minha versão é:fonte
Os métodos que conheço são os seguintes:
Math.sign (n)
var s = Math.sign(n)
Esta é a função nativa, mas é a mais lenta de todas devido à sobrecarga de uma chamada de função. No entanto, ele lida com 'NaN', onde os outros abaixo podem apenas assumir 0 (ou seja, Math.sign ('abc') é NaN).
((n> 0) - (n <0))
var s = ((n>0) - (n<0));
Neste caso, apenas o lado esquerdo ou direito pode ser 1 com base no sinal. Isso resulta em
1-0
(1),0-1
(-1) ou0-0
(0).A velocidade deste parece pescoço a pescoço com o próximo abaixo no Chrome.
(n >> 31) | (!! n)
var s = (n>>31)|(!!n);
Usa o "deslocamento para a direita de propagação de sinal". Basicamente, deslocar em 31 elimina todos os bits, exceto o sinal. Se o sinal foi definido, isso resulta em -1, caso contrário, é 0. À direita
|
dele testa positivo ao converter o valor em booleano (0 ou 1 [BTW: strings não numéricas, como!!'abc'
, tornam-se 0 neste caso, e not NaN]) então usa uma operação OR bit a bit para combinar os bits.Este parece ser o melhor desempenho médio entre os navegadores (melhor no Chrome e Firefox, pelo menos), mas não o mais rápido em TODOS eles. Por algum motivo, o operador ternário é mais rápido no IE.
n? n <0? -1: 1: 0
var s = n?n<0?-1:1:0;
Mais rápido no IE por algum motivo.
jsPerf
Testes realizados: https://jsperf.com/get-sign-from-value
fonte
Meus dois centavos, com uma função que retorna os mesmos resultados que Math.sign faria, ou seja, sinal (-0) -> -0, sinal (-Infinito) -> -Infinito, sinal (nulo) -> 0 , sinal (indefinido) -> NaN, etc.
Jsperf não me deixa criar um teste ou revisão, desculpe por não ser capaz de fornecer testes (eu tentei jsbench.github.io, mas os resultados parecem muito mais próximos uns dos outros do que com Jsperf ...)
Se alguém pudesse adicioná-lo a uma revisão Jsperf, eu ficaria curioso para ver como ele se compara a todas as soluções fornecidas anteriormente ...
Obrigado!
Jim.
EDITAR :
Eu deveria ter escrito:
(em
(+x && -1)
vez de(x && -1)
) a fim de lidarsign('abc')
corretamente (-> NaN)fonte
Math.sign não é compatível com o IE 11. Estou combinando a melhor resposta com Math.sign:
Agora, pode-se usar o Math.sign diretamente.
fonte