Число.sign () в javascript

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

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. очевидное и быстро!--17-->

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

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

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. искусство... но очень медленно!--30-->[медленный]

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

4. использование bit-shift
быстро, но!--12-->

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

5. Type-safe [megafast]

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

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

инструменты

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


[Offtopic] принят ответ

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

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

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

14 ответов


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

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

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

var sign = number && number / Math.abs(number);

функции вы ищете называется Сигнум, и лучший способ реализовать его:

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

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

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

это делает его совместимым с проектом математика ECMAScript.знак (MDN):

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

  • если x-NaN, то результат-NaN.
  • если x равно -0, то результат is -0.
  • если x равно +0, то результат равен +0.
  • если x отрицательно, а не -0, результат равен -1.
  • если x положительный, а не +0, результат равен +1.

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

в основном он возвращает -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

var sign = number >> 31 | -number >>> 31;

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


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

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

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


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

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

на вопрос "а +0 и -0 то же самое?" для получения дополнительной информации.


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


вы можете немного сдвинуть число и проверить самый значительный бит (MSB). Если MSB является 1, то число отрицательно. Если это 0, то число является положительным (или 0).


Я как раз собирался задать тот же вопрос, но пришел к решению, прежде чем я закончил писать, увидел, что этот вопрос уже существует, но не видел этого решения.

(n >> 31) + (n > 0)

вроде бы быстрее, добавив троичной хотя (n >> 31) + (n>0?1:0)


очень похоже на ответ Martijn является

function sgn(x) {
    isNaN(x) ? NaN : (x === 0 ? x : (x < 0 ? -1 : 1));
}

Я нахожу его более читабельным. Кроме того (или, в зависимости от вашей точки зрения, однако), он также groks вещи, которые могут быть интерпретированы как число; например, он возвращает -1 С '-5'.


Я не вижу никакого практического смысла возвращения -0 и 0 от Math.sign Итак, моя версия:

function sign(x) {
    x = Number(x);
    if (isNaN(x)) {
        return NaN;
    }
    if (x === -Infinity || 1 / x < 0) {
        return -1;
    }
    return 1;
};

sign(100);   //  1
sign(-100);  // -1
sign(0);     //  1
sign(-0);    // -1

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

математика.знак(Н)

var s = Math.sign(n)

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

((n>0) - (n

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

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

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

(n>>31) / (!!n)

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

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

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

n?n

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

самый быстрый в IE для некоторых причина.

см. Этот тест jsperf

выполненные тесты:https://jsperf.com/get-sign-from-value


мои два цента, с функцией, которая возвращает те же результаты, что и математика.знак будет делать, т. е. знак (-0) -- > -0, знак(-бесконечность) -- > - бесконечность, знак(null) -- > 0, знак(undefined) -- > NaN и т. д.

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

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

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

спасибо!

Джим.

редактировать:

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

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

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