Number.sign () в JavaScript - PullRequest
       40

Number.sign () в JavaScript

99 голосов
/ 02 октября 2011

Интересно, есть ли нетривиальные способы нахождения знака числа ( функция знака )?
Может быть короче / быстрее / элегантнее решения, чем очевидное

var sign = number > 0 ? 1 : number < 0 ? -1 : 0;

Краткая выдержка

Используйте это, и вы будете в безопасности и быстро

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}

Результаты

На данный момент у нас есть следующие решения:


1. Очевидный и быстрый

function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }

1.1. Модификация от kbec - один тип приведен меньше, более производительный, короче [самый быстрый]

function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }

Внимание: sign("0") -> 1


2. Элегантный, короткий, не такой быстрый [самый медленный]

function sign(x) { return x && x / Math.abs(x); }

Внимание: sign(+-Infinity) -> NaN, sign("0") -> NaN

Начиная с Infinity - это законный номер в JS, это решение кажется не совсем правильным.


3. Искусство ... но очень медленно [самое медленное]

function sign(x) { return (x > 0) - (x < 0); }

4. Использование бит-сдвига
быстро, но sign(-Infinity) -> 0

function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }

5. Тип безопасности [мегафаст]

! Похоже, что браузеры (особенно Chrome v8) делают некоторые магические оптимизации, и это решение оказывается намного более производительным, чем другие, даже чем (1.1), несмотря на то, что оно содержит 2 дополнительные операции и логически никогда не может не будет быстрее.

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}

Инструменты

Улучшения приветствуются!


[Offtopic] Принятый ответ

  • Андрей Таранцов - +100 за искусство, но, к сожалению, это примерно в 5 раз медленнее, чем очевидный подход

  • Фредерик Хамиди - каким-то образом самый одобренный ответ (на время написания), и это круто, но это определенно не то, как все должно быть сделано, имхо. Кроме того, он неправильно обрабатывает числа бесконечности, которые также являются числами.

  • kbec - улучшение очевидного решения. Не такой уж революционный, но, собрав все вместе, я считаю такой подход лучшим. Проголосуй за него:)

Ответы [ 14 ]

79 голосов
/ 31 января 2012

Более элегантная версия быстрого решения:

var sign = number?number<0?-1:1:0
28 голосов
/ 02 октября 2011

Деление числа на его абсолютное значение также дает его знак. Использование логического оператора короткого замыкания И позволяет нам использовать специальный случай 0, поэтому мы не делим его на:

var sign = number && number / Math.abs(number);
24 голосов
/ 20 января 2013

Функция, которую вы ищете, называется signum , и лучший способ ее реализации:

function sgn(x) {
  return (x > 0) - (x < 0);
}
13 голосов
/ 23 января 2014

Разве это не должно поддерживать подписанные нули JavaScript (ECMAScript)? Кажется, это работает при возврате x, а не 0 в функции «мегафаст»:

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? x : NaN : NaN;
}

Это делает его совместимым с черновиком Math.sign ECMAScript ( MDN ):

Возвращает знак x, указывающий, является ли x положительным, отрицательным или нулевым.

  • Если x равен NaN, результат равен NaN.
  • Если x равен −0, результат равен −0.
  • Если x равен +0, результат равен + 0.
  • Если x отрицательно и не равно -0, результат равен -1.
  • Если x положительно, а не +0, результат равен + 1.
10 голосов
/ 20 апреля 2015

Для тех, кому интересно, что происходит с последними браузерами, в версии ES6 есть собственный метод Math.sign .Вы можете проверить поддержку здесь .

В основном он возвращает -1, 1, 0 или NaN

Math.sign(3);     //  1
Math.sign(-3);    // -1
Math.sign('-3');  // -1
Math.sign(0);     //  0
Math.sign(-0);    // -0
Math.sign(NaN);   // NaN
Math.sign('foo'); // NaN
Math.sign();      // NaN
4 голосов
/ 24 апреля 2014
var sign = number >> 31 | -number >>> 31;

Сверхбыстрый, если вам не нужен Infinity и вы знаете, что число является целым числом, найденное в источнике openjdk-7: java.lang.Integer.signum()

1 голос
/ 05 января 2016

Решение, которое работает со всеми числами, а также 0 и -0, а также Infinity и -Infinity, имеет вид:

function sign( number ) {
    return 1 / number > 0 ? 1 : -1;
}

См. Вопрос " Являются ли +0 и -0 одинаковыми? " для получения дополнительной информации.


Предупреждение: Ни один из этих ответов, включая теперь уже стандартный Math.sign, не будет работать для дела 0 против -0. Возможно, это не проблема для вас, но в некоторых физических реализациях это может иметь значение.

1 голос
/ 24 августа 2014

Я думал, что добавлю это просто для удовольствия:

function sgn(x){
  return 2*(x>0)-1;
}

0 и NaN вернет -1
отлично работает на +/- Infinity

0 голосов
/ 12 февраля 2018

Мои два цента, с функцией, которая возвращает те же результаты, что и Math.sign, т.е. sign (-0) -> -0, sign (-Infinity) -> -Infinity, sign (null) --> 0, подписать (не определено) -> NaN и т. Д.

function sign(x) {
    return +(x > -x) || (x && -1) || +x;
}

Jsperf не позволит мне создать тест или ревизию, извините за невозможность предоставить вам тесты (яесли попробовать jsbench.github.io, но результаты кажутся гораздо ближе друг к другу, чем с Jsperf ...)

Если кто-то может добавить его в ревизию Jsperf, мне было бы интересно посмотреть, как она сравниваетсяко всем ранее приведенным решениям ...

Спасибо!

Джим.

РЕДАКТИРОВАТЬ :

Я должен был написать:

function sign(x) {
    return +(x > -x) || (+x && -1) || +x;
}

((+x && -1) вместо (x && -1)) для правильной обработки sign('abc') (-> NaN)

0 голосов
/ 01 октября 2017

Методы, которые я знаю, следующие:

Math.sign (п)

var s = Math.sign(n)

Это нативная функция, но самая медленная из-за накладных расходов при вызове функции. Однако он обрабатывает «NaN», где остальные ниже могут просто принять 0 (то есть Math.sign («abc») - это NaN).

((n> 0) - (n <0)) </strong>

var s = ((n>0) - (n<0));

В этом случае только левая или правая сторона могут быть 1 на основе знака. Это приводит к 1-0 (1), 0-1 (-1) или 0-0 (0).

Скорость этого кажется шеей и шеей со следующим ниже в Chrome.

(п >> 31) | (!! п)

var s = (n>>31)|(!!n);

Использует "Сдвиг вправо, распространяющий знаки". В основном сдвиг на 31 сбрасывает все биты, кроме знака. Если знак был установлен, это приводит к -1, в противном случае он равен 0. Справа от | он проверяет на положительное значение путем преобразования значения в логическое значение (0 или 1 [BTW: нечисловые строки, такие как !!'abc', становятся 0 в данном случае, а не NaN]), затем использует битовую операцию ИЛИ для объединения битов.

Это кажется лучшей средней производительностью во всех браузерах (по крайней мере, лучшей в Chrome и Firefox), но не самой быстрой во ВСЕХ из них. По какой-то причине троичный оператор работает быстрее в IE.

п п <0 -1:? 1: 0 </strong>

var s = n?n<0?-1:1:0;

Самый быстрый в IE по какой-то причине.

JSPerf

Проведенные испытания: https://jsperf.com/get-sign-from-value

...