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

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. С помощью bit-shift
быстро, но!--12-->

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

5. Type-safe [megafast]

! похоже, что браузеры (особенно V8 chrome) делают некоторые волшебные оптимизации, и это решение оказывается намного больше performant чем другие, даже чем (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 98

14 ответов:

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

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

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

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

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

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, то результат это -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;

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

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

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

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

решение, которое работает на всех числах, а также 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)

очень похож на ответ Мартейн составляет

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 [Кстати: нечисловые строки, например !!'abc', в этом случае становится 0, а не NaN]) затем использует побитовую операцию OR для объединения битов.

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

n?n

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

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

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

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

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

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

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

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

спасибо!

Джим.

EDIT:

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

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

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